Flask介绍
Flask是Python微框架,基于Werkzeuy和Jinja2实现。
Flask不包含数据库层,也不会包含表单库或是这个方向的其他东西。Flask建立在Werkezug和Jinja2的桥梁,前者实现一个合适的WSGI应用,后者处理模板。Flask也绑定了一些通用的标准库包,比如logging。其他所有一切取决于扩展。
Flask的思想是为所有应用建立一个良好的基础,其余的一切都取决于你和扩展。
Hello World
创建一个hello.py
文件
1 |
|
安装Flask
并执行hello.py
文件
1 |
|
If you are on Windows you need to use set
instead of export
.Flask
最新版本0.12.2。建议在Python3.x环境下运行。
注意: 上面的服务只能在你本地电脑才能访问,因为默认的debugging 模式下,应用的代码可以在你的电脑上运行。
如果你想让其他人电脑也可以访问你的这个服务,你只需要执行flask run --host=0.0.0.0
。
上面操作告诉系统允许所有公网IP访问。
Debug Mode
当开发时每当改变你的代码,Flask
就需要重启一次。Flask
提供了debug
模式支持当代码变动时自动重启应用。
开启debug
模式,你需要在允许服务器前定义FLASK_DEBUG
环境变量。
1 | $ export FLASK_APP=hello.py |
(在windows环境下export
需替换成set
)
上面代码完成了三个操作:
- 激活了debugger
- 激活了自动加载
- 开启了debug模式
从Flask
0.11开始你有多种构建方法运行开发服务。推荐使用flask
命令行模式,也可以继续使用Flask.run()
方法。
Command Line
1 |
|
这推荐使用的运行开发服务模式。你也可以通过传递参数给run
操作符。例如禁止重新加载:
1 |
|
如果不指定设置FLASK_APP
环境变量,可以通过flask run --reload
实现文件变动自动刷新
In Code
可以通过Flask.run()
方法启动服务。
1 |
|
上面如果在Debug模式下会导致一些奇怪的事情发生(一段代码执行两次,意外崩溃,死机当语法或导入错误)。所以在flask
0.11后推荐使用命令行模式启动服务。
注意:
1 | 尽管交互式调试器在允许 fork 的环境中无法正常使用(也即在生产服务器上正常使用几乎是不可能的),但它依然允许执行任意代码。这使它成为一个巨大的安全隐患,因此它 绝对不能用于生产环境 。 |
路由
Flask
路由系统继承于Werkzeug
路由系统进行了封住。
路由例子:
1 | from flask import Flask |
@app.route(rule, **options)
装饰器,将url规则于视图函数进行绑定。当url规则匹配到将触发对应的方法。
也可以通过app.add_url_rule(rule, endpoint=None, view_func=None, **options)
方法手动将路由规则和视图函数进行绑定。
1 | def cat (): |
变量规则
URL上可以添加变量部分,你只需要标记为<variable_name>
。
变量部分将通过定义的变量关键字通过参数传递给视图函数。
1 |
|
一个视图函数可以绑定多个路由装饰器,如果路由装饰器有变量部分,可以在视图函数指定默认变量值。
1 |
|
可以将变量部分转换为指定的类型,只需将变量部分定义成<converter:name>
1 |
|
支持的转换类型有这些:
类型 | 描述 |
---|---|
string | 接收任何类型 除了斜杠 |
int | 接收整数 |
float | 接收浮点数 |
path | 像默认,但也接受斜杠 |
any | 匹配提供的项目之一 |
uuid | 接收 uuid字符串 |
HTTP请求类型
可以指定视图函数触发的请求类型,这样可以根据不同请求类型进行不同处理或者触发不同视图函数。
1 |
|
支持的请求类型有:
- GET - 浏览器告诉服务器想获取一些信息。
- HEAD - 浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好了。
- POST - 浏览器告诉服务器:想在 URL 上 发布 新信息。并且,服务器必须确保 数据已存储且仅存储一次。这是 HTML 表单通常发送数据到服务器的方法。
- PUT - 类似 POST 但是服务器可能触发了存储过程多次,多次覆盖掉旧值。你可 能会问这有什么用,当然这是有原因的。考虑到传输中连接可能会丢失,在 这种 情况下浏览器和服务器之间的系统可能安全地第二次接收请求,而 不破坏其它东西。因为 POST 它只触发一次,所以用 POST 是不可能的。
- DELETE - 删除给定位置的信息。
- OPTIONS - 给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实现了自动处理。
构造URL
如果 Flask 能匹配 URL,那么 Flask 可以生成它们吗?当然可以。你可以用 url_for() 来给指定的函数构造 URL。它接受函数名作为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾作为查询参数。例子:
1 | from flask import Flask, url_for |
唯一URL/重定向行为
Flask 的 URL 规则基于 Werkzeug 的路由模块。这个模块背后的思想是基于 Apache 以及更早的 HTTP 服务器主张的先例,保证优雅且唯一的 URL。
1 |
|
虽然它们看起来着实相似,但它们结尾斜线的使用在 URL 定义 中不同。 第一种情况中,指向 projects 的规范 URL 尾端有一个斜线。这种感觉很像在文件系统中的文件夹。访问一个结尾不带斜线的 URL 会被 Flask 重定向到带斜线的规范 URL 去。
然而,第二种情况的 URL 结尾不带斜线,类似 UNIX-like 系统下的文件的路径名。访问结尾带斜线的 URL 会产生一个 404 “Not Found” 错误。
这个行为使得在遗忘尾斜线时,允许关联的 URL 接任工作,与 Apache 和其它的服务器的行为并无二异。此外,也保证了 URL 的唯一,有助于避免搜索引擎索引同一个页面两次。
请求
Flask
中http请求会绑定在全局变量request
上。
在一个视图函数通过request
对象获取请求数据。
1 | from flask import request |
request.form['xxx']
获取表单提交的数据(POST或PUT请求)request.method
获取请求方法request.path
获取请求地址request.args.get('key','')
获取URL上参数request.cookies.get('username')
获取username
cookie值
响应
从视图函数返回的值会自动转换成响应对象。如果返回值是一个字符串,它会转换成响应对象并且字符串作为响应主体。
Flask转换返回值为响应对象的逻辑如下:
- 如果返回是正确类型的响应对象,则直接从视图函数返回。
- 如果是个字符串,使用这个数据和默认参数创建响应对象。
- 如果是个元组,这个元组提供额外的信息。这个元组形式必须是
(response,status,headers)
或(response,headers)
最少有一项是在元组中。status
会覆盖状态码。headers
能够是个列表或一个字典补充头部信息。 - 如果上面条件都没满足,
Flask
会假定返回一个合肥的WSGI
应用值,然后将它转换响应对象。
返回视图
1 | from flask import render_template |
render_template(template,data)
渲染模板函数。
Flask
会去templates 目录查找模板(hello.html
)。如果你的应用是一个模块,这个模板文件夹应该和这个模块在一个层级上。:
1 | /application.py |
如果你的应用是个包,则这个模板文件夹应该在包里面:
1 | /application |
返回JSON
如果在视图函数直接返回一个dict
对象,Flask
会报错(TypeError: 'dict' object is not callable
)。
可以通过jsonify
方法对dict
对象进行包装再返回。
1 | from flask import Flask, jsonify |
添加headers信息 flask.make_response
有时我们需要在视图头部添加一些信息。视图不需要返回一个响应对象,因为Flask会自动将它转换成响应对象。可以调用flask.make_response()
方法而不是返回,你获取一个响应对象,这样你就可以添加头部信息:
1 | def index(): |
通过Response返回JSON数据
1 | from flask import Response |
蓝图(Blueprint)
Flask用于实现模块化组织程序结构的方法。
在开发一个web项目时,通常使用两种方式组织架构:
- 功能式架构 - 在功能式架构中,按照每部分代码的功能来组织你的应用。所有模板放到同一个文件夹中,静态文件放在另一个文件夹中,而视图放在第三个文件夹中。
1 | yourapp/ |
- 分区式架构 - 在分区式架构中,按照每一部分所属的蓝图来组织你的应用。
1 | yourapp/ |
将相关联的部分作为一个blueprint
,这样遍历管理和多人协同开发。最后只需要将blueprints
汇集起来注册到Flask
。
创建一个Blueprint
1 | # -*- coding: utf-8 -*- |
Blueprint(name, import_name)
类接受参数name
和import_name
进行初始化。通常用__name__
表示当前模块的特殊Python变量,作为import_name
的取值。
如果你使用分区式架构,你得告诉Flask
某个蓝图有自己得模板和静态文件夹你需要另外传两个参数template_folder
和static_folder
:
1 | admin = Blueprint('admin', __name__, |
注册Blueprint
1 | from flask import Flask |
register_blueprint(blueprint,url_prefix)
将blueprint注册到Flask应用中,并给blueprint定义一个公用得url前缀。
即插视图(Pluggable Views)
将视图函数转为成为类,以便于扩展和通用。例如:
1 |
|
上面视图函数简单而灵活,但如果需要同样适应其他模型或模板得方式,你需要更大得灵活性。你需要基于类得即插视图。
1 | from flask.views import View |
你需要创建一个 flask.views.View
的子类,并且实现dispatch_request()
。然后需要通过as_view()
类方法将类转换到一个实际的视图函数。你传的这个函数的字符串是视图之后的终极名称,在Flask内部将通过这个endpoint找到对应的view function。
方法提示
即插视图可以像常规函数用route()
或add_url_rule()
附加到应用上。你可以在类定义methods=[]
指定视图函数接收的请求类型。
1 | class MyView(View): |
flask.views.MethodView
通过MethodView
类可以方便的在处理相同路径下不同请求方式情况。每个 HTTP 方法映射到同名函数 (只有名称为小写的)
1 | from flask.views import MethodView |
配置处理
在实际开发中一般会面对不同的环境,每个环境都会自己的配置项。Flask
提供了app.config.from_object()
,app.config.from_pyfile()
,app.config.from_envvar()
方法用于不同环境加载不同配置项。
app.config.from_object()
app.config.from_object(object)
接收一个python对象。实际开发中推荐为不同环境创建不同的配置文件:
1 | |config |
在启动文件可以这样调用
1 | from flask import Flask |
1 | DEBUG=True |
app.config.from_pyfile()
有时你需要定义一些不能为人所知的配置变量。为此,你会想要把它们从config.py中的其他变量分离出来,并保持在版本控制之外。 你可能要隐藏类似数据库密码和API密钥的秘密,或定义特定于当前机器的参数。 为了让这更加轻松,Flask提供了一个叫instance文件夹的特性。 instance文件夹是根目录的一个子文件夹,包括了一个特定于当前应用实例的配置文件。我们不要把它提交到版本控制中。
这是一个使用了instance文件夹的简单Flask应用的结构:
1 | config.py |
要想加载定义在instance文件夹中的配置变量,你可以使用app.config.from_pyfile()
。 如果在调用Flask()创建应用时设置了instance_relative_config=True
,app.config.from_pyfile()
将查看在instance文件夹的特殊文件。
1 | app = Flask(__name__, instance_relative_config=True) |
现在,你可以在instance/config.py
中定义变量,一如在config.py。 你也应该将instance文件夹加入到版本控制系统的忽略名单中。比如假设你用的是git,你需要在gitignore中新开一行,写下instance/。
app.config.from_envvar()
你可以在命令行模式设定环境变量,这个环境变量应该是配置文件的绝对路径 。
1 | from flask import Flask |
1 | APP_CONFIG_FILE=/var/www/yourapp/config/production.py |
插件
Flask-Migrate
Flask-Migrate用于对SQLAlchemy数据迁移进行操作的Flask插件。
安装
1 | pip install Flask-Migrate |
例子
1 | from flask import Flask |
对上面的应用,可以使用下面命令创建迁移仓库:
1 | flask db init |
执行完后在应用目录下会多个migrations 文件夹。文件夹里面的内容需要和其它源文件一起添加进你的版本控制。
你可以生成初始迁移:
1 | flask db migrate |
Flask-Migrate是基于Alembic
。它有一些限制:表名变化,字段名变化,匿名命名约束它是无法检测到。
通过下面命令可以将迁移应用到数据:
1 | flask db upgrade |
每次迁移操作都应该添加进行版本控制中。
每次数据模型有变化,重复上面migrate
和upgrade
命令。
Flask-Login
Flask-Login
为Flask提供用户会话管理,处理用户的登录,登出,登录状态判断。
它可以处理:
- 会话中存储激活了的用户ID,让你登录或登出更简单。
- 让你限制登入(或登出)用户可以访问的视图。
- 处理棘手的
记住我
功能。 - 保护用户会话避免被盗。
- 可以于
Flask-Principal
或其它认证扩展集成。
它无法处理:
- 限制你使用特定的数据库或其它存储方法。如何加载用户完全由你决定。
- 限制你使用用户名和密码,OpenIDs,或者其它的认证方法。
- 处理超越 “登入或者登出” 之外的权限。
- 处理用户注册或者账号恢复。
安装
1 | pip install flask-login |
基础用法
1 | from flask import Flask |
保护视图 flask.ext.login.login_required(func)[source]
1 |
|
上面方法只有用户登录后才能调用。
定制登录 Request Loader
在调用接口时,我们需要将请求验证密钥,这密码一般放在请求头部。
通过request_loader
方法我们可以创建一个解析请求头部信息方法。
1 |
|
记住我
当用户关闭浏览器后,会话就当结束。当打开网页用户需要重新登录才能获取到用户信息。通过login_user(remeber= True)
[http://www.pythondoc.com/flask-login/#flask.ext.login.login_user]设置,当用户登录过一次后,会在用户计算机中存储一个cookie。当用户在打开网页时会读取cookie信息恢复用户ID。cookie是防篡改的,当用户改过它,它就会被拒绝。
新鲜登录
如果用户通过登录获取用户信息Flask-Login
会将用户标记为fresh
。如果是通过cookie获取到用户信息会被标记为non-fresh
。
login-required
视fresh
或non-fresh
一样的效果。当如果有些操作比如:修改用户信息则应该只允许fresh
操作,如果是non-fresh
用户应该让他登录。
fresh_login_required
装饰的方法只有fresh
用户才能调用。login_manager.refresh_view='auth.login'
指定non-fresh
登录视图方法。login_manager.needs_refresh_message = ( u"To protect your account, please reauthenticate to access this page." )
重新登录文案提示。
会话保护
你可以在 LoginManager 上和应用配置中配置会话保护。如果它被启用,它可以在 basic 或 strong 两种模式中运行。要在 LoginManager
上设置它,设置 session_protection
属性为 “basic” 或 “strong”:
1 | login_manager.session_protection = "strong" #None 、basic |
默认,它被激活为 “basic” 模式。它可以在应用配置中设定 SESSION_PROTECTION 为 None 、 “basic” 或 “strong” 来禁用。
当启用了会话保护,每个请求,它生成一个用户电脑的标识(基本上是 IP 地址和 User Agent 的 MD5 hash 值)。如果会话不包含相关的标识,则存储生成的。如果存在标识,则匹配生成的,之后请求可用。
在 basic 模式下或会话是永久的,如果该标识未匹配,会话会简单地被标记为非活 跃的,且任何需要活跃登入的东西会强制用户重新验证。(当然,你必须已经使用了活跃登入机制才能奏效。)
在 strong 模式下的非永久会话,如果该标识未匹配,整个会话(记住的令牌如果存在,则同样)被删除。
Flask-Session
原生flask实用cookie签名保存session,而该插件支持将session保存到多个地方,如:redis、memcached、mongodb。
安装
1 | $ pip install flask-session |
保存到redis
1 | # -*- coding:utf-8 - |
作者: Fynn
链接: https://fynn90.github.io/2017/12/09/Flask%E4%BB%8B%E7%BB%8D/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可