同文館

Flask Web开发笔记

#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。

  1. 易用性
  2. 性能
  3. 可移植性
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
7.6 单元测试
7.7 创建数据库
8.1 Flask的认证扩展
8.2 密码安全性