Flask小结

Web本质为了利用互联网交流工作文档。

Web框架:

1、协助开发者快速开发 Web 应用程序的一套功能代码

2、开发者只需要按照框架约定要求,在指定位置写上自己的业务逻辑代码

框架的优点:

稳定性和可扩展性强

可以降低开发难度,提高开发效率

常见的框架:flask, django, tornado

Flask

 

Flask是用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展

两大核心: Werkzeug(路由模块)模板引擎Jinja2

虚拟环境:

虚拟环境可以搭建独立的python运行环境, 使得单个项目的运行环境与其它项目互不影响

创建:mkvirtualenv 虚拟环境名称

退出:deactivate

删除:rmvirtualenv

创建项目:

  1. 导入flask类
  2. Flask函数接收一个参数__name__,它会指向程序所在的包
  3. 添加装饰器将路由映射到视图函数
  4. Flask应用程序实例的run方法,启动web服务器

程序运行过程中,程序实例中会使用 url_map 将装饰器路由和视图的对应关系保存起来

相关配置参数:

Flask 程序初始化参数

Flask 程序相关配置加载方式

app.run() 参数

初始化参数:

static_url_path静态文件访问路径,可以不传

static_folder静态文件存储的文件夹,可以不传

template_folder模板文件存储的文件夹,可以不传

程序加载配置

从配置对象中加载(常用)app.config.from_object()

配置文件中加载app.config.from_pyfile()

从环境变量中加载(了解)app.config.from_envvar()

读取配置

App.config.get()

在视图函数中使用curren_app.config.get()

App.run():参数:host="0.0.0.0", port=5000, debug = True

 

路由:

指定访问路径为 demo1

@app.route('/demo1')

def demo1():

    return 'demo1'

路由传参:

# 路由传递参数

@app.route('/user/')

def user_info(user_id):

    return 'hello %s' % user_id

Int:实际上代表使用 IntegerConverter 去处理 url 传入的参数

指定请求方式:

Postman:

是一款功能强大的网页调试与发送网页 HTTP 请求的 Chrome 插件,可以直接去对我们写出来的路由和视图函数进行调试,作为后端程序员是必须要知道的一个工具。

视图常用逻辑:

返回Json

在使用 Flask 写一个接口时候需要给客户端返回 JSON 数据,在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应

重定向:

重定向到自己写的视图函数

可以直接填写自己 url 路径 

也可以使用 url_for 生成指定视图函数所对应的 url

自定义状态码:

在 Flask 中,可以很方便的返回自定义状态码,以实现不符合 http 协议的状态码,

正则匹配路由:

1、导入转换器基类,在flask中,所有的路由的匹配规则都是使用转换器对象进行记录from werkzeug.routing import BaseConverter

2、自定义转换器,继承于转换器基类

class RegexConverter(BaseConverter):

def __init__(self, url_map, *args):

super(RegexConverter,self)__init__(url_map):

将接收到的第一个参数作为匹配规则进行保存

self.regex = args[0]

  1. 添加转换器到默认转换器字典中

app.url_map.converters[re] = RegexConverter

4、使用自定义转换器实现自定义匹配规则

 

 

系统自带转换器:6种

 'default':    'string':       UnicodeConverter,

    'any':              AnyConverter,

    'path':             PathConverter,

    'int':              IntegerConverter,

    'float':            FloatConverter,

    'uuid':             UUIDConverter,

 

异常捕获:

abort 方法

抛出一个给定状态代码的 HTTPException 或者 指定响应

抛出状态码的话只能抛出HTTP协议的错误状态码

捕获错误:

erroerhandle装饰器

注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法

捕获指定异常:

@app.errorhandler(ZeroDivisionError)

def zero_division_error(e):

    return '除数不能为0'

请求钩子:

为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

Before_first_request

Before_request

After_request

Teardown_request

装饰器路由具体实现:

Werkzeug是一个遵循WSGI协议的python函数库Werkzeug库的 routing 模块负责实现 URL 解析。不同的 URL 对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,执行该函数以此生成一个响应信息。

Routing模块内部含有:

  1. rule类:用来构造不同的URL模式的对象,路由URL规则
  2. Map类:存储所有的url规则和一些配置参数
  3. BaseConverter的子类:复制定义匹配规则
  4. MapAdapter类:复制协调Rule做具体的匹配的工作

 

Request:

是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)

:

获取上传的内容:request.files.get()

状态保持:

无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。

实现状态保持:客户端:cookie,

服务端:session

Cookie是存储在浏览器中的一段纯文本信息,由浏览器第一次访问服务器时,服务器设置的;当浏览器再次访问服务器时会带上cookie并提交给对应的服务器,因此,cookie是在response中设置(set_cookie),在request中获取(request.cookies.get())

Session:当浏览器访问服务器时,对于敏感,重要的信息要存储在服务器中,如:密码,金额等,在服务端进行状态保持:session,session依赖于cookie

 

上下文

请求上下文:request、session

Request:可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,request封装了HTTP请求的内容,针对的是http请求

Session:用来记录请求回话中的信息,针对的是用户信息

 

应用上下文:即并不是一直存在的,它是伴request而生,随request而死的

Current_app:用于存储应用程序中的变量, 可通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量

g变量:是flask程序中全局的一个临时变量,可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别

两者区别:

请求上下文:保存了客户端和服务器交互的数据

应用上下文:flask应用程序运行过程中,保存的一些配置信息

 

Flask_script:可以从flask_script中导入Manager类,使用manager来管理程序的运行。即:manager= Manager(app), manager.run()

 

 

模板承担视图函数的另一个作用返回响应内容。

  1. 是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
  2. 使用真实值来替换变量, 在返回最终得到的字符串, 此为“渲染”
  3. Flask使用jinja2模板引擎来进行渲染模板。

Jinja2

是由Python实现的语言,被广泛应用的模板引擎。

渲染模板函数:

flask提供的render_template函数封装了该模板引擎

Render_template函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。

使用:

{{ }}:变量代码块,用来表示变量名

{% %} 控制代码块,用来实现一些语言层次的功能,如:循环,if等

{# #} 进行注释。

模板的使用:

在项目下创建templates文件夹,用于存放模板文件,并创建html文件

在视图函数中将该模板内容进行渲染返回

过滤器:其本质就是函数

有时候我们不仅仅只是需要输出变量的值,我们还需要修改变量的显示,甚至格式化、运算等等,而在模板中是不能直接调用 Python 中的某些方法,那么这就用到了过滤器。

使用方式:变量名 | 过滤器

链式调用:{{ hello world | reverse | upper}}  先进行翻转再将其进行大写转换

字符串操作:safe(禁止转义), capitalize, lower, upper,title, reverse

列表操作:first(取第一个元素), last, length, sum, sort(排序)

 

自定义过滤器:

  1. 通过Flask应用对象的 add_template_filter 方法
  2. 通过装饰器来实现自定义过滤器

注:自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。

方式一:通过调用应用程序实例的 add_template_filter 方法实现自定义过滤器。该方法第一个参数是函数名,第二个参数是自定义的过滤器名称

 

app.add_template_filter(do_listreverse,'lireverse')

 

 

方式二:装饰器实现

@app.template_filter('lireverse')  参数为自定义过滤器名称

def do_listreverse(li):

    # 通过原列表创建一个新列表

    temp_li = list(li)

    # 将新列表进行返转

    temp_li.reverse()

return temp_li

在html中使用:
my_array 原内容:{{ my_array }}

 
my_array 反转:{{ my_array |
lireverse }}

 

控制代码块:if/else  /   endif

For  /  endfor

 

模板代码复用:

使用jinja2中的宏,继承和包含来实现

宏:(macro)

是jinja2中的一个函数, 返回一个模板或者html字符串

为了避免出现代码冗余,可以将相同的代码写成函数进行复用

需要在多处重复使用的模板代码片段写入单独的文件,再包含在模板中,避免 重复。

定义宏:

{% macro input(name,value='',type='text') %}

    

        value="{{value}}" class="form-control">

{% endmacro %}

 

把宏封装成html文件,在其他模板文件中先导入,再调用。

模板继承:

为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。

标签定义的内容:{% block top%}    

 {% endblock%}

父模板:base.html

子模板:extends指令声明子模板继承自哪

{% extends base.html %}

{% block content %} 需要填充的内容

 {% endblock  content %}

 

模板继承注意点:

不支持多继承,

子模板使用extends尽量写在模板的第一行

不能在一个模板文件中定义多个相同名字的block标签

包含:将另一个模板整个加载到当前模板中,并直接渲染。

Include使用{% include  hello world.html  %}

如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上 ignore missing 关键字

 

代码复用总结:

宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。

宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。

继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。

包含(include)是直接将目标模板文件整个渲染出来。

模板特有的变量和函数:可以在自己的模板中访问一些flask内置的一些函数和对象

Config:

Request:

Session:

G变量:

Url_for:

Get_flashed——messages()

Web表单:

三部分:表单标签,表单域, 表单提交按钮

在Flask中,为了处理web表单,我们可以使用 Flask-WTF 扩展,它封装了 WTForms,并且它有验证表单数据的功能

使用 Flask-WTF 需要配置参数 SECRET_KEY。

CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。

CsrfCross Site Request Forgery译为跨站请求伪造。

 

防止csrf攻击:

1、在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值

2、在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token

3、在用户点击提交的时候,会带上这两个值向后台发起请求

4、后端接受到请求,会做以下几件事件:

从 cookie中取出 csrf_token

从 表单数据中取出来隐藏的 csrf_token 的值进行对比

5、如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作

同源策略因为浏览器的同源策略:网站 B 获取不到表单中的 csrf_token 的隐藏字段,因此就解决了跨站请求伪造的问题。

在flask-form中设置应用程序的secret_key,用于加密生成csrf_token值

 

App.secret_key = 此处可以写随机的字符串

并在模板的表单中添加: {{ form.csrf_token() }}

 

ORM

Object_relationship_mapping 对象-关系映射

优点 :

1、只需要面向对象编程, 不需要面向数据库编写代码.

对数据库的操作都转化成对类属性和方法的操作.

不用编写各种数据库的sql语句.

2、实现了数据模型与数据库的解耦, 屏蔽了不同数据库操作上的差异.

不在关注用的是mysql、oracle...等.

通过简单的配置就可以轻松更换数据库, 而不需要修改代码.

缺点 :

  1. 相比较直接使用SQL语句操作数据库,有性能损失.
  2. 根据对象的操作转换成SQL语句,根据查询的结果转化成对象, 在映射过程中有性能损失.

 

数据库链接设置

在 Flask-SQLAlchemy 中,数据库使用URI指定,而且程序使用的数据库必须保存到Flask配置对象的 SQLALCHEMY_DATABASE_URI 键中

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/数据库的名称'

其他设置:

# 动态追踪修改设置,如未设置只会提示警告

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True#查询时会显示原始SQL语句

app.config['SQLALCHEMY_ECHO'] = True

数据库基本操作:

在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。

会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 commit() 方法提交会话。

 

在 Flask-SQLAlchemy 中,查询操作是通过 query 对象操作数据。

最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

 

 

 

数据库中多对多演练

多对多关系描述有一个唯一的点就是:需要添加一张单独的表去记录两张表之间的对应关系

数据库迁移:

在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。

更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。

在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。需要安装:Flask-Migrate。

为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。

创建迁移仓库

python database.py db init会创建migrations文件夹,所有迁移文件都放在里面。

创建迁移脚本:

upgrade():函数把迁移中的改动应用到数据库中。

downgrade():函数则将改动删除。

更新数据库:python  database.py  db  upgrade

实际操作顺序:

1.python 文件 db init

2.python 文件 db migrate -m"版本名(注释)"

3.python 文件 db upgrade 然后观察表结构

4.根据需求修改模型

5.python 文件 db migrate -m"新版本名(注释)"

6.python 文件 db upgrade 然后观察表结构

7.若返回版本,则利用 python 文件 db history查看版本号

8.python 文件 db downgrade(upgrade) 版本号

 

信号机制:

Flask信号(signals, or event hooking)允许特定的发送端通知订阅者发生了什么

蓝图

Blueprint:

模块化,随着flask程序越来越复杂,我们需要对程序进行模块化的处理,之前学习过python的模块化管理,于是针对一个简单的flask程序进行模块化处理

概念:Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求。

蓝图对象没有办法独立运行,必须将它注册到一个应用对象上才能生效

使用蓝图三步骤:

  1. 创建一个蓝图(初始化蓝图):admin=Blueprint(admin, __name__)
  2. 在蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器

@admin.route(/)

  1. 在应用对象上注册这个蓝图对象:app.register_blueprint(admin, url\prefix=/admin)

运行机制

蓝图是保存了一组将来可以在应用对象上执行的操作,注册路由就是一种操作

当在应用对象上调用 route 装饰器注册路由时,这个操作将修改对象的url_map路由表

然而,蓝图对象根本没有路由表,当我们在蓝图对象上调用route装饰器注册路由时,它只是在内部的一个延迟操作记录列表defered_functions中添加了一个项

当执行应用对象的 register_blueprint() 方法时,应用对象将从蓝图对象的 defered_functions 列表中取出每一项,并以自身作为参数执行该匿名函数,即调用应用对象的 add_url_rule() 方法,这将真正的修改应用对象的路由表

 

在项目中创建一个包:cart,里面包含__init__文件和view文件,init文件只负责导入蓝图和创建蓝图,并不编写业务逻辑代码,view文件中只编写视图函数

,最后由主文件来导入cart包,在主文件中app中注册蓝图app.register_blueprint(cart_blu)

 

 

单元测试:

Web程序开发过程一般包括以下几个阶段:[需求分析,设计阶段,实现阶段,测试阶段]。其中测试阶段通过人工或自动来运行测试某个系统的功能。目的是检验其是否满足需求,并得出特定的结果,以达到弄清楚预期结果和实际结果之间的差别的最终目的。

分类:单元测试、集成测试、系统测试

单元测试是由开发人员进行的,而其他测试都由专业的测试人员来完成

 

在Web开发过程中,单元测试实际上就是一些“断言”(assert)代码。

单元测试的基本写法:

  1. 首先,定义一个类,继承自unittest.TestCase

import unittest

class TestClass(unitest.TestCase):

    pass

 

  1. 在测试类中,定义两个测试方法

#该方法会首先执行,方法名为固定写法

def setUp(self):

app.testing = True

         pass

    #该方法会在测试代码执行完后执行,方法名为固定写法

    def tearDown(self):

        pass

  1. 在测试类中,编写测试代码

#测试代码

    def test_app_exists(self):

        pass

 

断言就是判断一个函数或对象的一个方法所产生的结果是否符合你期望的那个结果。 python中assert断言是声明布尔值为真的判定,如果表达式为假会发生异常。单元测试中,一般使用assert来断言结果。

 

Nosql:

  1. 泛指非关系型数据库

2、不支持SQL语法

3、存储结构跟传统关系型数据库中的那种关系表完全不同,nosql中存储的数据都是KV形式

4、NoSQL的世界中没有一种通用的语言,每种nosql数据库都有自己的api(应用程序编程接口)和语法,以及擅长的业务场景

5、NoSQL中的产品种类相当多:

    1. Mongodb
    2. Redis
    3. Hbase hadoop
    4. Cassandra hadoop

Nosql和sql的区别:

  1. sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
  2. sql对事务的支持非常完善,而nosql基本不支持事务
  3. 两者不断在取长补短,呈现融合趋势

Redis

开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

特性:

  1. 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  2. 不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  3. 支持数据的备份,即master-slave模式的数据备份。

优势:

性能极高:读的速度是110000次/s,写的速度是81000次/s 。

数据类型丰富:支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

丰富的特性:Redis还支持 publish/subscribe, 通知, key 过期等等特性。

 

应用场景:

用作缓存:redis的所有数据是放在内存中的(内存数据库)

在某些特定应用场景下替代传统数据库——比如社交类的应用

在一些大型系统中,巧妙地实现一些特定的功能:session共享、购物车

核心配置:

绑定本机:127.0.0.1

端口:6379

是否以守护进程运行:yes为守护进程daemonize yes

数据文件:dbfilename dump.rdb

数据库:默认有16个

主从复制:类似于双机备份:slaveof

服务器端的命令:redis-server

可以使用--help查看帮助文档   redis-server  --help

查看redis服务器的进程:ps  aux | grep redis

Sudo kill -9 pid (pid:redis的进程id)杀死redis服务器

客户端:redis-cli

使用ping命令测试连接情况,成功为:pang

切换数据库:数据库没有名字,默认为16个,0~15,默认选择第一个,可使用select  3 来选择

数据结构:key-value的数据结构

注:键的类型是字符串,且不能重复

值的类型有五种:

字符串:string

哈希:hash

列表:list

集合:set(无序集合)

有序集合:zset

 

String类型是redis最为基础的数据存储类型

保存:如键不存在,则为添加,如已存在,则为修改

设置键值:set key value

设置过期时间:以秒为单位:setex key second value

设置多个键值:mset key1 value1 key2 value2........

追加值:append  key  value (在值的后面追加)

获取:get key ,如键不存在返回为nil

一次获取多个值:mget  key1  key2

删除:删除键时,值也会被删除 :del  key1  key2

键命令:

查看所有键:keys *

查看名称中包含a的键:keys a*

判断键是否存在:exists key1

查看键对应的value类型 type key

设置过期时间:expire key seconds

查看有效时间:ttl key

与python 交互:

第一步:导入 from redis import *

通过init 创建对象,host:默认localhost,port:6379,db:0

Sr = StrictRedis(host = localhost, port = 6379, db=0 )

简写成:sr = StrictRedis()

增删改查:

From redis import *

If __name__ == __main__

Try:

创建redis对象,与redis服务器建立连接

sr = StrictRedis()

添加键name,值为itheima,成功为True,失败为False

Result = sr.set(name, itheima)

获取值:键存在则返回对应的值,如键不存在,则返回none

Resut = sr.get(name)

修改值:如果键存在,则修改,如不存在,则添加

Result = sr.set(name, itheima)

删除:如删除成功则返回受影响的键数,否则返回0

Result = sr.delete(name)

获取所有的键,如有则返回一个列表,没有键返回空列表

Result = sr.keys()

Except Exception as e:

Print(e)

 

主从搭建:

一个master可以拥有多个slave, 一个slave可以拥有多个slave

Master用来写数据,slave用来读数据

Master 和slave都是redis的实例(redis服务)

搭建集群:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

项目开发

项目分析

Web开发项目:新经资讯网,基于flask框架,前后端不分离

技术实现:

数据存储:MySQL+ redis实现

第三方扩展:七牛云 + 云通讯

功能模块:

  1. 新闻模块:首页 + 详情
  2. 用户模块:登录, 注册, 个人信息修改 + 新闻收藏, 发布
  3. 后台管理

注册

用户账号为手机号

图片验证码正确后才能发送短信验证码

短信验证码每60秒发送一次

条件出错时有相应的错误提示

登录

用手机号与密码登录

错误时有相应的提示

项目框架搭建

使用 Pycharm 为项目设置 Git 版本控制

完成项目基本配置:

抽取代码,熟悉项目基本目录结构

创建manager.py文件,实现初级的index页面

使用git管理源代码

  1. 初始化 git:

cd【项目目录】

Git  init

  1. 配置当前项目git提交信息

Git config user.(name xxx / user.email)

  1. 添加忽略文件

 Touch.gitignore

  1. 添加所有文件到暂存区

Git add .

  1. 提交到本地仓库并填写注释

Git commit -m 第一次提交

  1. 让pycharm管理当前项目的git
  2. 远程提交:使用码云
  3. 上传项目到码云
  4. 回滚代码

回滚到上一版本:git reset -- hard HEAD~1

查看所有提交版本记录:git reflog

回到指定版本:git reset --hard提交id

项目基本配置

Config类:在当前类中定义配置的信息,并从中加载配置

SQLAlchemy:导入数据库扩展,并在配置中填写相关配置

Redis:配置redis的host和port:6379

Csrf:包含请求体的请求都需要开启csrf

Session:利用flask-session扩展,将 session 数据保存到 Redis 中

SESSION_REDIS = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT) # 使用 redis 的实例

Flask-Script于数据库迁移扩展:利用manager来管理app

 

代码抽取:

1、 manage.py 同级目录下创建 config.py 文件,用作于项目的配置文件,在manager.py 中引入 Config 类,直接使用

2、业务逻辑独立:

逻辑文件放在一个单独的文件夹内info,与 manage.py 同级

3、项目多种配置

一个web程序在开发阶段可能与生产阶段所需要的配置信息可能不一样,所以为了实现此功能,可以给不同情况创建不同的配置类:DevelopementConfig 和ProdutionConfig

  1. 工厂类方法创建应用实例: config.py中定义配置字典, 在info的init文件中添加create-app的工厂方法

日志作用:

程序调试

了解软件程序运行情况,是否正常

软件程序运行故障分析与问题定位

如果应用的日志信息足够详细和丰富,还可以用来做用户行为分析

日志等级:

.NOTSET(没有设置)

DEBUG (调试)

INFO(信息)

WARNING(警告)

ERROR(错误)

CRITICAL 重大的,危险的

日志功能的实现:

Python 自身提供了一个用于记录日志的标准库模块:logging

开发应用程序或部署开发环境时,可以使用 DEBUG 或 INFO 级别的日志获取尽可能详细的日志信息来进行开发或部署调试;

应用上线或部署生产环境时,应该使用 WARNING 或 ERROR 或 CRITICAL 级别的日志来降低机器的I/O压力和提高获取错误日志信息的效率。

集成日志到当前项目

首先在不同环境的配置下添加日志级别

在init文件中添加日志配置的相关方法

在项目根目录下创建日志目录文件夹 logslogs 文件夹下创建 .gitkeep 文件,以便能将 logs 文件夹添加到远程仓库,并在 .gitignore 文件中添加忽略提交生成的日志文件

创建蓝图目录

项目的蓝图模块可以按照两种方式来分:

按功能模块分:如用户模块和订单模块

按接口版本分:某个版本的接口放在一个文件夹下面

本项目使用功能模块来划分蓝图:

  1. 在info目录下创建modules包(info存放所有的业务逻辑,modules存放所有的工具类)
  2. 在modules文件夹下创建index文件夹,再在index文件及下创建views文件并将主程序中的路由拷贝到views中,views中存放当前模块所有的视图函数
  3. 在index中的init文件中创建自己的蓝图
  4. 在views中导入蓝图,并使用该蓝图注册路由
  5. 将蓝图注册到主app中

数据库表的分析与创建

数据库的迁移

$ python manage.py db init

$ python manage.py db migrate -m"initial"

$ python manage.py db upgrade

再添加测试数据

静态文件的导入

将前端人员开发好的news和admin文件拖到info下的static文件夹下

创建模板目录

与static同级创建templates文件夹(存放所有的模板文件), 并在此文件夹下创建news文件夹用于存放新闻前台模板文件

添加模板并创建根路由视图函数

使用Pycharm 将 static/news/index.html 文件移动到 templates/news/ 目录下,并在 index.py 中添加根路由访问视图

网站图标显示:

自定义一个视图函数,send_static_file 是系统访问静态文件所调用的方法

新闻前台

注册登陆:

功能分析:

使用手机号当前用户名进行注册

注册的时候需要使用短信验证码进行验证

在发送短信验证码之前需要验证图片验证码,以防止恶意发送短信验证码

登陆注册模块蓝图

 modules 文件夹下创建 Package 取名为 passport,并在此目录下创建views.py 文件(在此模块中封装注册和登陆等功能)

passport 模块下的 __init__.py 文件中创建当前模块所使用的蓝图

再在app中注册该蓝图

 

图片验证码

流程:

1、前端页生成验证码编号,并将编号并提交到后台去请求验证码图片

2、后台生成图片验证码,并把验证码文字内容当作值,验证码编号当作key存储在 redis 中

3、后台把验证码图片当作响应返回给前端

4、前端申请发送短信验证码的时候带上第1步验证码编号和用户输入的验证码内容

5、后台取出验证码编号对应的验证码内容与前端传过来的验证码内容进行对比

6、如果一样,则向指定手机发送验证码,如果不一样,则返回验证码错误

前端代码实现:

\info\static\news\js\main.js 中使用generateImageCode 方法

实现步骤:(浏览器方需生成的验证码编号)

1、通过uuid生成验证码编号: imageCodeId = generateUUID();

2、拼接验证码地址var imageCodeUrl = "/passport/image_code?code_id=" + imageCodeId;

3、设置验证码图片标签的src$(".get_pic_code").attr("src", imageCodeUrl)

服务端的响应:

 passport 目录下的 view.py 文件中添加获取验证码的路由 image_code

  1. 获取参数; 2、生成验证码; 3、删除之前验证码并保存当前验证码
  1. 错误处理; 5、响应返回

 

@passport-bul.route(/image_code)

def get_image_code():

Pass

安装图片验证码所需要的 Pillow 模块

info 下创建 utils Package

captcha 文件夹和 response_code.py 文件拷贝到该目录

在视图函数中:

1、首先获取到当前的图片编号id:request.args.get('code_id')

2、生成验证码:name, text, image = captcha.generate_captcha()

3、保存当前生成的图片验证码内容到redis中:redis_store.setex

4、返回响应内容resp = make_response,

并在响应头中设置:resp.headers['Content-Type'] = 'image/jpg'

在前端main.js文件中指定js点击回调生成验证码的函数

在代码末尾添加generateImageCode()

云通讯:

拷贝项目资料文件到indo/lib/yuntongxun目录下

passport/views.py 文件中,注册 smscode 路由

接口设计:

URL:/passport/sms_code

请求方式:POST

传入参数:JSON格式

参数:mobile, image_code, image_code_id

返回json 格式类型

 

发送短信后端逻辑:

   1. 接收参数并判断是否有值

    2. 校验手机号是正确

    3. 通过传入的图片编码去redis中查询真实的图片验证码内容

    4. 进行验证码内容的比对

    5. 生成发送短信的内容并发送短信

    6. redis中保存短信验证码内容

7. 返回发送成功的响应

前端代码逻辑:

main.js 文件的 sendSMSCode 方法中,使用 ajax 进行发送短信请求

暂时关闭 csrf_token 的校验,去 init/__init__.py 文件中注释 csrf_token 保护功能,否则会报错:csrf token is missing

 

注册后端逻辑:

接口设计:

URL:/passport/register

请求方式:POST

传入参数:JSON格式

参数

passport/views.py 中添加 register 视图函数

1. 获取参数和判断是否有值

    2. 从redis中获取指定手机号对应的短信验证码的

    3. 校验验证码

    4. 初始化 user 模型,并设置数据并添加到数据库

    5. 保存当前用户的状态

6. 返回注册的结果

注册前端实现:

main.js 中完善注册表单提交的逻辑,同样使用ajax

登录后端实现:

接口设计:

URL:/passport/login

请求方式:POST

传入参数:JSON格式

参数: mobile, password

代码实现:

info/passport/views.py 文件中添加登录的路由

1. 获取参数和判断是否有值

    2. 从数据库查询出指定的用户

    3. 校验密码

    4. 保存用户登录状态

    5. 返回结果

注意:配置 SQLAlchemy SQLALCHEMY_COMMIT_ON_TEARDOWN 属性实现请求结束之后进行数据自动提交

 

登录前端同样是在main.js文件中完善表单提交的逻辑

 

首页右上角内容逻辑显示

未登录时:显示登录、注册按钮

登录时:显示用户的头像和昵称,并提示退出入口

代码实现:

在index/views中对原有代码进行改造。

并在index.html中使用模板语言进行逻辑判断

 

 

 {# 判断用户是否登录 #}

        {% if data.user_info %}

            {# 如果登录,则显示用户信息 #}

   {% else %}

            {# 如果没有登录,则显示登录注册按钮 #}

完善CSRFToken逻辑: 

在flask中,只针对app进行CSRFToken校验操作,步骤:

  1. 生成 csrf_token 的值

导入生成CSRFToken 值的函数

调用函数生成CSRFToken

  1. 将 csrf_token 的值传给前端浏览器

使用请求钩子函数完成此逻辑

  1. 在前端请求时带上 csrf_token 值

根据登录和注册的业务逻辑,当前采用的是 ajax 请求

所以在提交登录或者注册请求时,需要在请求头中添加 X-CSRFToken 的键值对

退出登录实现

本质为:删除之前登录保存在服务器中的session数据

代码实现:

在passport中的views。Py文件中添加退出视图函数:logout

 

新闻首页

 

需求:

  1. 中间展示新闻分类信息
  2. 右侧显示新闻点击排行
  3. 根据分类进行新闻列表展示
  4. 上啦加载更多数据
  5. 点击新窗口跳转到新闻详情页

点击排行展示

在请求跟路由时去数据库查询按点击量排行的10条新闻

排名前三条的序号有颜色高亮:

采用自定义过滤器的形式对sapn标签的class 指定:在utils目录下创建common.py文件,

新闻分类展示:

在请求跟路径的时候去查询新闻分类,并默认第一个分类选中,

新闻列表数据

  • 新闻列表数据只是当前页面的一部分
  • 点击分类时需要去获取当前分类下的新闻数据
  • 并在展示的时候需要更新新闻列表界面,不需要整体页面刷新
  • 所以新闻数据也使用 ajax 的方式去请求后台接口进行获取

 

 

 

后端实现:

在index/views.py中定义视图

  1. 获取参数   request.args
  2. 检验参数
  3. 查询参数, 并分页:

如果分类id不为1, 那么添加分类的id过滤

获取查询出来的数据

获取到总页数

  1. 返回数据

新闻详情:

创建新闻详情页的模块news,并在该模块下创建视图函数views,

创建蓝图,注册蓝图

基类模板的抽取:

在模板文件夹下创建base.html

将首页内容全部拷贝到base.html中,将特有的东西留作block给子模板进行实现

抽取出来的block为:

标题

Script内容

顶部内容

页面内容

点击排行div中的作者信息

详情页数据的展示:

如果当前用户已登录,查询当前登录用户数据,并在渲染模板时返回

首页视图函数也存在相同代码,都是如果用户登录就获取用户登录数据,没有登录就返回None,所以可以进行代码抽取。

实现思路:

使用装饰器去加载用户数据并记录到 g 变量

在当前请求中可以直接使用 g 变量取到用户数据

g 变量是一个应用上下文变量,类似于一个全局变量,但是 g 变量里面的保存的值是相对于每次请求的,不同的请求,g 变量里面保存的值是不同的,所以同一次请求,可以使用 g 变量来保存值用于进行函数的传递。

新闻详情页数据的展示:

新闻收藏功能:

进入到新闻详情页之后,如果用户已收藏该新闻,则显示已收藏,点击则为取消收藏,反之点击收藏该新闻

代码实现:

  1. 前端根据后端返回的数据显示收藏和取消收藏
  2. 后端提供收藏和取消收藏的接口
  3. 前端发起收藏和取消收藏的请求

 

 

 

收藏后端实现:

Url:/news/news_collect

方式:post

数据格式:json

请求参数:news_id, action

返回数据:errno,errmsg

新闻评论:

分析:

用户如果在登录的情况下,可以进行评论,未登录,点击评论弹出登录框,

用户可以直接评论当前新闻,也可以回复别人发的评论

用户A回复用户B的评论之后,用户A的评论会当做一条主评论进行显示,下面使用灰色框将用户B的评论显示,

在detail.js文件中添加代码, 实现未登录的情况下点击评论弹出提示框

评论新闻后端:

新闻评论:

新闻点赞的功能:

分析:

  1. 后端提供点赞和取消短暂的功能:
  2. 当用户点击未点赞按钮,执行点赞的逻辑,当点击取消点赞时则反之
  3. 当新闻显示完成之后,底部评论会根据当前登录用户显示是否点赞图标

接口:

Url:/news/comment_like

请求方式:POST

传入参数:JSON

参数:

Comment_id, news_id, action

返回参数类型:JSON

伪代码实现:

获取参数, 判断参数, 查询评论数据,执行相关操作

前端实现:

给点赞的a标签添加自定义属性,以记录当前点赞的新闻id和评论id供点赞和取消点赞的请求使用

点赞计数

个人中心

个人中心是使用 iframe 进行实现,左边各个页签对应不同的 html 子页面,那么,子页面更新数据之后要同步更新主页面相关联数据,可以采用 js 进行,所以当前页面数据交互还是采用 ajax 方式

 

因为个人中心页面和新闻详情也一样,顶部和底部内容相似,可以继承之前抽取的base.html,

 

个人基本资料设置

分析:

进入界面之后展示用户的基本资料(个性签名、昵称、性别)

用户修改之后点击保存,向服务器发送请求进行数据保存更新

后端接口实现:

请求url:user/base_info

请求方式:post

传入参数:JSON格式

参数:nick_name, signature,gender

返回数据:JSON格式:errno , errmsg

用户基本信息:

  1. 获取用户登录信息
  2. 获取传入的参数
  3. 更新并保存数据
  4. 返回结果

用户头像上传:

分析:

用户可以进行头像的修改,上传完毕之后更新个人中心跟头像相关的标签内容

上传的头像文件保存到【七牛云】提供的文件存储服务器中

七牛云存储:

作用:

用于在实际项目中存储媒体(图像、音频、视频)文件

节省自己服务器空间,节约宽带,提升媒体文件访问的稳定性

不需要人力物力对重复数据、冗余数据进行清理及判断

 

后端接口:

URL:/user/pic_info

请求方式:POST

传入参数:JSON格式

用户密码修改:

URL:/user/pass_info

请求方式:POST

传入参数:JSON格式

用户新闻收藏:

需求分析:

使用分页的形式展示用户所收藏的新闻

可以使用 url 的查询字符串传入不同的页数进行模板渲染不同的数据

可以支付使用模板显示,也可以使用 ajax 进行请求

 

用户发布新闻:

 

 

需求分析:

用户可以在个人中心发布新闻

发布完毕新闻需要通过审核才能显示

用户发布完毕新闻跳转到用户新闻列表

后台管理员对新闻进行审核

如果某条新闻是用户发布的,那么在新闻详情页展示该用户的个人资料

需要更新新闻查询条件,添加 status == 0(已审核通过)

接口实现:

接口:

URL:/user/news_release

请求方式:POST

传入参数:JSON格式

用户新闻列表:

需求分析:

URL:/user/news_release

请求方式:POST

传入参数:JSON格式

 

新闻后台

 

功能需求:

通过命令行形式创建管理员用户(flask-script扩展)

管理员与普通用户共用一张表,管理员用户也具有普通用户的功能,是否是管理员使用指定字段区分

管理员用户可以登录到后台进行相应的数据查看以及新闻操作

打开管理员相关操作页面如果未登录或者角色不是管理员需要跳转到管理员登录页面

 

管理员登录:

需求分析:

管理员用户进行登录,并且根据不同的情况报出不同的错误信息

如果当前已登录用户是管理员,在访问登录页面时直接跳转到后台管理主页

登录界面可以直接使用 Form 表单提交(也可以采用 ajax 的方式)

 

在 templates 目录下创建 admin 文件夹,将 static/admin/目录下 login.html 与 index.html 拖动到 admin 目录下

 

管理后台主页:

需求:为后台提供专门的视图函数

需要带入当前管理员用户信息以便在用户界面展示

 

后台访问权限控制

需求:

解决普通用户登录之后直接访问后台具体的视图函数的问题

如果是普通用户访问后台的视图函数,直接跳转到项目主页,不再执行后续的逻辑判断

后台后续要实现多个视图函数,如果每一个函数内部都去判断用户权限,那么代码重复率高,冗余代码较多

所以得有一个统一判断入口,后台模块中,除了登录页面,后台的其他页面都要判断是否具有管理员权限

采用的方式为:请求勾子中的 before_request,来请求之前进行判断

 

用户统计:

需求:

展示当前的总人数, 月活跃人数,日活跃人数

使用图表的形式展示活跃曲线

代码实现思路:

月新增数:获取到本月第1天0点0分0秒的时间对象,然后查询最后一次登录比其大的所有数据

日新增数:获取到当日0点0分0秒时间对象,然后查询最后一次登录比其大的所有数据

图表查询:遍历查询数据每一天的数据(当前天数,减去某些天)

新闻管理:

新闻审核需求分析:

以分页的形式按新闻创建时间倒序展示出待审核的新闻数据

可以使用关键字对新闻标题进行搜索

点击审核进入审核详情页面(对新闻只能查看不能编辑)

审核不通过需要写明不通过原因

新闻版式编辑:

需求分析:

以分页的形式按新闻创建时间倒序展示出新闻数据

可以使用关键这这对新闻标题进行搜索

点击编辑进入编辑详情页面

 

新闻分类管理:

可以修改当前新闻的分类名:

可以添加新闻分类

 

 

 

 

 

 

项目部署:

 

基于ubuntu16.04系统, 使用Gunicorn + Ngin进行部署,阿里云服务器

Nginx:

采用C语言编写,

实现分流, 转发, 负载均衡

Gunicorn:

Gunicorn(绿色独角兽)是一个Python WSGI的HTTP服务器

从Ruby的独角兽(Unicorn )项目移植

该Gunicorn服务器与各种Web框架兼容,实现非常简单,轻量级的资源消耗

Gunicorn直接用命令启动,不需要编写配置文件

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(项目,flask小结)