已经解释的非常详细了,我这里只把我关注的点以我的理解记录一下。
一:程序的基本结构
1. 初始化
2. 路由和视图函数
- flask使用route装饰器把一个函数绑定到对应的 URL 上,把修饰的函数注册为路由
- URL 使用尖括号 <variable_name> , flask将这个部分作为命名参数传递到你的函数。可以给该参数定义转换器实现类型转换。
转换器又:int,float,path
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % username
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % post_id
3. 程序和请求的上下文
Flask使用在 Flask 中由全局的
request
对象来提供这些信息客户端交互信息。Flask 从客户端收到请求时,要先让视图函数能访问一些对象,这样才能处理请求。例如request对象封装了客户端发送的 HTTP 请求。
Flask 使用上下文临时把某些对象变为全局可访问。
-
在 Flask 中有两种上下文:程序上下文和请求上下文
Flask 在分发请求之前激活(或推送)程序和请求上下文,请求处理完成后再将其删除
在程序实例上调用 app.app_context() 可获得一个程序上 下文。
Flask 中的某些对象是全局对象,但却不是通常的那种。这些对象实际上是特定环境的局部对象的代理。虽然很拗口,但实际上很容易理解。
想象一下处理线程的环境。一个请求传入,Web 服务器决定生成一个新线程( 或者别的什么东西,只要这个底层的对象可以胜任并发系统,而不仅仅是线程)。 当 Flask 开始它内部的请求处理时,它认定当前线程是活动的环境,并绑定当前的应用和 WSGI 环境到那个环境上(线程)。它的实现很巧妙,能保证一个应用调用另一个应用时不会出现问题。
所以,这对你来说意味着什么?除非你要做类似单元测试的东西,否则你基本上可以完全无视它。你会发现依赖于一段请求对象的代码,因没有请求对象无法正常运行。解决方案是,自行创建一个请求对象并且把它绑定到环境中。单元测试的最简单的解决方案是:用 <tt class="xref py py-meth docutils literal" style="font-family: Consolas, Menlo, "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 0.9em; background-color: rgb(251, 251, 251); color: rgb(34, 34, 34); font-weight: bold; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: white;">test_request_context()</tt> 环境管理器。结合 <cite>with</cite> 声明,绑定一个测试请求,这样你才能与之交互
4. 请求调度
- 程序收到客户端发来的请求时,要找到处理该请求的视图函数。为了完成这个任务,Flask 会在程序的 URL 映射中查找请求的 URL。URL 映射是 URL 和视图函数之间的对应关系。 Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。
- URL 映射中的 HEAD、Options、GET 是请求方法,由路由进行处理。Flask 为每个路由都指 定了请求方法,这样不同的请求方法发送到相同的 URL 上时,会使用不同的视图函数进 行处理。
5. 请求钩子
- 有时需要在处理请求之前或之后执行代码。例如,认证发起请求的用户。为了避免在每个视图函数中都使用重复的代码, Flask 提供了注册通用函数的功能,注册的函数可在请求被分发到视图函数之前或之后调用。这种程序叫做钩子函数。
- 请求钩子使用修饰器实现
- flask支持以下几种钩子
- before_first_request:注册一个函数,在处理第一个请求之前运行
- before_request:注册一个函数,在每次请求之前运行
- after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行
- teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
- 在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g,例如,before_ request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视 图函数时,视图函数再使用 g.user 获取用户。
6. 响应
- Flask 视图函数一般返回一个 Response 对象。make_response() 函数可接受 1 个、2 个或 3 个参数(和视图函数的返回值一样),并返回一个 Response 对象。
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
- 重定向是一种特殊的响应,响应内容是 URL,而不是包含 HTML 代码的字符串。浏览器收到 这种响应时,会向重定向的 URL 发起 GET 请求,显示页面的内容。
- 重定向响应可以使用 3 个值形式的返回值生成,也可在 Response 对象中设定。不过,由于使用频繁,Flask 提 供了 redirect() 辅助函数,
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
- 可以使用redirect() 辅助函数, 生成 HTTP 重定向响应。redirect() 函数的参数是重定向的 URL,推荐使用 url_for() 生成 URL,因为这 个函数使用 URL 映射生成 URL,从而保证 URL 和定义的路由兼容,而且修改路由名字后 依然可用。
- url_for() 函数的第一个且唯一必须指定的参数是端点名,即路由的内部名字。默认情 况下,路由的端点是相应视图函数的名字。
- 重定向时请求数据会request上下文的内容会丢失,因为重定向相当于另一个请求,这时程序可以把数据存储在session中,在请求之间“记住”数据。session是一种私有存 储,存在于每个连接到服务器的客户端中。默认情况下,session保存在客户端 cookie 中
from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
flask扩展
社区成员开发了大量不同用途的flask扩展,下面介绍如何把扩展整合到程序中
- 使用pip安装flask扩展
- 在程序中使用
from flask.ext.script import Manager
flask.ext模块导入该扩展
- 专为 Flask 开发的扩展都暴漏在 flask.ext 命名空间下
二:模版
- 默认情况下,Flask 在程序文件夹中的 templates 子文件夹中寻找模板
- flask支持很多模版引擎,
render_template
函数把Jinja2
模板引擎集成到了程序中 -
render_template
函 数的第一个参数是模板的文件名。随后的参数都是键值对,表示模板中变量对应的真实值
from flask import Flask, render_template # ...
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
1. 变量
- 在模板中使用的
{{ name }}
结构表示一个变量,它是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取 - 可以使用过滤器修改变量,过滤器名添加在变量名之后,中间使用竖线分隔
-
Jinja2 提供的部分常用过滤器:
2. jinja2控制结构
- Jinja2 提供了多种控制结构,可用来改变模板的渲染流程
条件控制语句
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
for循环
<ul>
{% for comment in comments %}
<li>{{ comment }}</li> {% endfor %}
</ul>
引入别的文件
{% include 'common.html' %}
- 需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免 重复:
- 模版继承
base.html
<html>
<head>
{% block head %} //声明一个代码块
<title>{% block title %}{% endblock %} - My Application</title> {% endblock %}
</head>
<body>
{% block body %}
{% endblock %} </body>
</html>
{% extends "base.html" %} 声明这个模板衍生自 base.html。
{% block title %}Index{% endblock %} {% block head %} //覆盖父类代码块
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %} <h1>Hello, World!</h1> {% endblock %}
推荐使用flask-bootstrap框架
- Flask-Bootstrap 中的基模板提供了一个网页框架,引入了 Bootstrap 中的所有 CSS 和JavaScript 文件。
3. 自定义错误显示页面
- Flask 允许程序使用基于模板的自定义错误页面
4.链接
- Flask 提供了 url_for() 辅助函数,它可以使用程序 URL 映射中保存 的信息生成 URL
- url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用 的端点名)作为参数,返回对应的 URL。
- 使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如,
url_for ('user', name='john', _external=True) 的返回结果是 http://localhost:5000/user/john。
。传入 url_for() 的关键字参数不仅限于动态路由中的参数。函数能将任何额外参数添加到 查询字符串中,例如,url_for('index', page=2) 的返回结果是 /?page=2。
5.静态文件
- 默认设置下,Flask 在程序根目录中名为 static 的子目录中寻找静态文件
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon"> {% endblock %}
四:web表单
- request.form 能获取 POST 请求中提交的表单数据。
- Flask-WTF扩展可以把处理 Web 表单的过程变成一 种愉悦的体验。
1. 跨站请求伪造保护
- 默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(CSRF)的攻击
- 为了实现 CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
- app.config 字典可用来存储框架、扩展和程序本身的配置变量。
4.2 表单类
- 类定义表单中的 一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来 验证用户提交的输入值是否符合要求。
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()]) submit = SubmitField('Submit')
//字段构造函数的第一个参数是把表单渲染成 HTML 时使用的标号
//StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受 用户提交的数据之前验证数据。验证函数 Required() 确保提交的字段不为空
3.把表单渲染成HTML
- 表单字段是可调用的,在模板中调用后会渲染成 HTML。
- Flask-Bootstrap 提供了一个非常高端的辅助函 数,可以使用 Bootstrap 中预先定义好的表单样式渲染整个 Flask-WTF 表单
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %} <div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div>
{{ wtf.quick_form(form) }} {% endblock %}
- 在视图函数中处理表单
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm() 在视图函数中创建一个 NameForm 类实例用于表示表单
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
- Flash消息
- flash() 函数
七:大型程序的结构
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config: #基类 Config 中包含通用配置,子类分别定义专用的配置。
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig,
'default': DevelopmentConfig }
- 为了让配置方式更灵活且更安全,某些配置可以从环境变量中导入。例如,SECRET_KEY 的值, 这是个敏感信息,可以在环境中设定,但系统也提供了一个默认值,以防环境中没有定义。
- 配置类可以定义 init_app() 类方法,其参数是程序实例。
2. 蓝本blueprint
- 蓝本和程序类似,也可以定义路由。不同的 是,在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序 的一部分。使用位于全局作用域中的蓝本时,定义路由的方法几乎和单脚本程序一样
- 蓝本可以在单个文件中定义,也可使用更结构化的方式在包中的多个模块中创建
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
- 程序的路由保存在包里的 app/main/views.py 模块中,而错误处理程序保存在 app/main/ errors.py 模块中。导入这两个模块就能把路由和错误处理程序与蓝本关联起来
参考链接:
Flask 文档