Flask笔记
2.1初始化
Web 服务器使用一种名为 Web 服务器网关接口
(Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。程序实例是 Flask 类的对象
2.2 路由和视图函数
程序实例需要知道对每个 URL 请求运行哪些代码,所以保存了一个URL到
Python 函数的映射关系。处理 URL 和函数之间关系的程序称为路由。
在 Python 代码中嵌入响应字符串会导致代码难以维护
2.3启动服务器
name=='main' 是 Python 的惯常用法,在这里确保直接执行这个脚本时才启动开发Web 服务器。如果这个脚本由其他脚本引入,程序假定父级脚本会启动不同的服务器,因此不会执行 app.run()。
服务器启动后,会进入轮询,等待并处理请求。轮询会一直运行,直到程序停止,比如按Ctrl-C 键。
2.5请求-响应循环
2.5.1 程序和请求上下文
Flask 使用上下文临时把某些对象
变为全局可访问。
变量名 | 上下文 | 说明 |
---|---|---|
current_app | 程序上下文 | 当前激活程序的程序实例 |
g | 程序上下文 | 处理请求时用作临时存储的对象。每次请求都会重设这个变量 |
request | 请求上下文 | 请求对象,封装了客户端发出的 |
session | 请求上下文 | 用户会话,用于存储请求之间需要“记住”的值的词典 |
2.5.2 请求调度
程序收到客户端发来的请求时,要找到处理该请求的视图函数。为了完成这个任务,Flask
会在程序的 URL 映射中查找请求的 URL。URL 映射是 URL 和视图函数之间的对应关系。
Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。
2.5.3 请求钩子
有时在处理请求之前或之后执行代码会很有用
- before_first_request:注册一个函数,在处理第一个请求之前运行。
- before_request:注册一个函数,在每次请求之前运行。
- after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
- teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
2.5.4 响应
Flask 调用视图函数后,会将其返回值作为响应的内容。大多数情况下,响应就是一个简
单的字符串,作为 HTML 页面回送客户端。
但 HTTP 协议需要的不仅是作为请求响应的字符串。HTTP 响应中一个很重要的部分是状
态码,Flask 默认设为 200,这个代码表明请求已经被成功处理。
3.1 Jinja2模板引擎
3.1.1 渲染模板
Flask 提供的 render_template 函数把 Jinja2 模板引擎集成到了程序中。render_template 函
数的第一个参数是模板的文件名
3.1.2 变量
在模板中使用的 {{ name }} 结构表示一个变量,它是一种特殊的占位符,告诉模
板引擎这个位置的值从渲染模板时使用的数据中获取。
Jinja2 能识别所有类型的变量,甚至是一些复杂的类型,例如列表、字典和对象。在模板
可以使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔。例如,下述
模板以首字母大写形式显示变量 name 的值:
Hello, {{ name|capitalize }}
3.1.3 控制结构
下面这个例子展示了如何在模板中使用条件控制语句:
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
另一种常见需求是在模板中渲染一组元素。下例展示了如何使用 for 循环实现这一需求:
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
Jinja2 还支持宏。宏类似于 Python 代码中的函数。
需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免
重复:
{% include 'common.html' %}
3.2 使用Flask-Bootstrap集成Twitter Bootstrap
Bootstrap(http://getbootstrap.com/)是 Twitter 开发的一个开源框架,它提供的用户界面组
件可用于创建整洁且具有吸引力的网页,而且这些网页还能兼容所有现代 Web 浏览器。
3.3 自定义错误页面
像常规路由一样,Flask 允许程序使用基于模板的自定义错误页面。最常见的错误代码有
两个:404,客户端请求未知页面或路由时显示;500,有未处理的异常时显示。为这两个
错误代码指定自定义处理程序的方式如示例 3-6 所示。
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
3.4 链接
在模板中直接编写简单路由的 URL 链接不难,但对于包含可变部分的动态路由,在模板
中构建正确的 URL 就很困难。而且,直接编写 URL 会对代码中定义的路由产生不必要的
依赖关系。如果重新定义路由,模板中的链接可能会失效。
为了避免这些问题,Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存的信息生成 URL。
3.5 静态文件
默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件。如果需要,可在static 文件夹中使用子文件夹存放文件。
3.6 使用Flask-Moment本地化日期和时间
服务器需要统一时间单位,这和用户所在的地理位置无关,所以一般使用协调世界时
(Coordinated Universal Time,UTC).不过用户看到 UTC 格式的时间会感到困惑,他们更希望看到当地时间,而且采用当地惯用的格式。
一个优雅的解决方案是,把时间单位发送给 Web 浏览器,转换成当地时间,然后渲染。
4.1 跨站请求伪造保护
默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,
CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发 CSRF 攻击。
为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成
加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法如示例 4-1 所示。
4.2 表单类
使用 Flask-WTF 时,每个 Web 表单都由一个继承自 Form 的类表示。这
4.3 把表单渲染成HTML
表单字段是可调用的,在模板中调用后会渲染成 HTML。假设视图函数把一个 NameForm 实例通过参数 form 传入模板,在模板中可以生成一个简单的表单,如下所示:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
4.4 在视图函数中处理表单
不仅要渲染表单,还要接收表单中的数据
4.5 重定向和用户会话
程序可以把数据存储在用户会话中,在请求之间“记住”数据。用户会话是一种私有存储,存在于每个连接到服务器的客户端中
4.6 Flash消息
请求完成后,有时需要让用户知道状态发生了变化。这里可以使用确认消息、警告或者错误提醒。
仅调用 flash() 函数并不能把消息显示出来,程序使用的模板要渲染这些消息。最好在基模板中渲染Flash 消息,因为这样所有页面都能使用这些消息。Flask 把get_flashed_messages() 函数开放给模板,用来获取并渲染消息
5.1 SQL数据库
关系型数据库把数据存储在表中,表模拟程序中不同的实体。
表的列数是固定的,行数是可变的。列定义表所表示的实体的数据属性。
表中有个特殊的列,称为主键,其值为表中各行的唯一标识符。表中还可以有称为外键的列,引用同一个表或不同表中某行的主键。行之间的这种联系称为关系,这是关系型数据库模型的基础。
5.2 NoSQL数据库
所有不遵循上节所述的关系模型的数据库统称为 NoSQL 数据库。
使用 NoSQL 数据库当然也有好处。数据重复可以提升查询速度。列出用户及其角色的操作很简单,因为无需联结
5.3 使用SQL还是NoSQL
SQL 数据库擅于用高效且紧凑的形式存储结构化数据。这种数据库需要花费大量精力保证数据的一致性。NoSQL 数据库放宽了对这种一致性的要求,从而获得性能上的优势。
5.4 Python数据库框架
Flask 并不限制你使
用何种类型的数据库包,因此可以根据自己的喜好选择使用 MySQL、Postgres、SQLite、Redis、MongoDB 或者 CouchDB。
- 易用性
- 性能
- 可移植性
5.5 使用Flask-SQLAlchemy管理数据库
Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用 SQLAlchemy 的操作。
5.6 定义模型
模型这个术语表示程序使用的持久化实体。
5.7 关系
关系型数据库使用关系把不同表中的行联系起来。
5.8 数据库操作
5.8.1 创建表
方法是使用 db.create_all()
5.8.2 插入行
db.session.add(admin_role)
5.8.3 修改行
db.session.add(admin_role)
db.session.commit()
5.8.4 删除行
db.session.delete(mod_role)
db.session.commit()
5.8.5 查询行
Flask-SQLAlchemy 为每个模型类都提供了 query 对象
5.9 在视图函数中操作数据库
5.10 集成Python shell
每次启动 shell 会话都要导入数据库实例和模型,这真是份枯燥的工作。为了避免一直重复导入,我们可以做些配置,让 Flask-Script 的 shell 命令自动导入特定的对象。
5.11 使用Flask-Migrate实现数据库迁移
7.1 项目结构
多文件 Flask 程序的基本结构
|-flasky
|-app/
|-templates/
|-static/
|-main/
|-__init__.py
|-errors.py
|-forms.py
|-views.py
|-__init__.py
|-email.py
|-models.py
|-migrations/
|-tests/
|-__init__.py
|-test*.py
|-venv/
|-requirements.txt
|-config.py
|-manage.py
这种结构有 4 个顶级文件夹:
- Flask 程序一般都保存在名为 app 的包中;
- 和之前一样,migrations 文件夹包含数据库迁移脚本;
- 单元测试编写在 tests 包中;
- 和之前一样,venv 文件夹包含 Python 虚拟环境
同时还创建了一些新文件:
- requirements.txt 列出了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境;
- requirements.txt 列出了所有依赖包,便于在其他电脑中重新生成相同的虚拟环境;
- manage.py 用于启动程序以及其他的程序任务。
7.2 配置选项
程序经常需要设定多个配置。这方面最好的例子就是开发、测试和生产环境要使用不同的数据库,这样才不会彼此影响。
7.3 程序包
程序包用来保存程序的所有代码、模板和静态文件。我们可以把这个包直接称为app
7.3.1 使用程序工厂函数
在单个文件中开发程序很方便,但却有个很大的缺点,因为程序在全局作用域中创建,所以无法动态修改配置。运行脚本时,程序实例已经创建,再修改配置为时已晚。这一点对单元测试尤其重要,因为有时为了提高测试覆盖度,必须在不同的配置环境中运行程序。
这个问题的解决方法是延迟创建程序实例,把创建过程移到可显式调用的工厂函数中。这种方法不仅可以给脚本留出配置程序的时间,还能够创建多个程序实例,这些实例有时在测试中非常有用。
7.3.2 在蓝本中实现程序功能
蓝本和程序类似,也可以定义路由。不同的
是,在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分。使用位于全局作用域中的蓝本时,定义路由的方法几乎和单脚本程序一样。
7.4 启动脚本
顶级文件夹中的 manage.py 文件用于启动程序。
7.5 需求文件
程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。如果要在另一台电脑上重新生成虚拟环境,这个文件的重要性就体现出来了,例如部署程序时使用的电脑。pip 可以使用如下命令自动生成这个文件:
(venv) $ pip freeze >requirements.txt