Flask-WTF扩展可以把处理Web表单的过程变成一种愉悦的体验。
安装命令
pip install flask-wtf
跨站请求伪造保护
Flask-WTF能保护所有表单免受跨站请求伪造(CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时就会引发CSRF攻击。
为了实现CSRF,Flask-WTF需要程序设置一个密钥。Flask-WTF使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。
设置Flask-WTF
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
app.config
字典可用来存储框架、扩展和程序本身的配置变量。使用标准的字典句法就能把配置值添加到app.config
对象中。
表单类
使用Flask-WTF时,每个Web表单都由一个继承自Form的类表示。这个类定义表单中的一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用来验证用户提交的输入值是否符合标准。
定义表单类
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class NameForm(Form):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
字段构造函数的第一个参数是把表单渲染成HTML时使用的标号。
StringField
构造函数中的可选参数validators
指定一个由验证函数组成的列表,在接受用户提交的数据之前验证数据。验证函数DataRequired()
确保提交的字段不为空。
WTForms支持的HTML标准字段
字段类型 | 说明 |
---|---|
StringField | 文本字段 |
TextAreaField | 多行文本字段 |
PasswordField | 密码文本字段 |
HiddenField | 隐藏文本字段 |
DateField | 文本字段,值为datetime.date格式 |
DatetimeField | 文本字段,值为datetime.datetime格式 |
IntegerField | 文本字段,值为整数 |
DecimalField | 文本字段,值为decimal.Decimal |
FloatField | 文本字段,值为浮点数 |
BooleanField | 复选框,值为True和False |
RadioField | 一组单选框 |
SelectField | 下拉列表 |
SelectMultipleField | 下拉列表,可选择多个值 |
FileField | 文件上传字段 |
SubmitField | 表单提交按钮 |
FormField | 把表单作为字段嵌入另一个表单 |
FieldList | 一组指定的类型的字段 |
WTForms验证函数
验证函数 | 说明 |
---|---|
验证电子邮件地址 | |
EqualTo | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
IPAddress | 验证IPv4网络地址 |
Length | 验证输入字符串的长度 |
NumberRange | 验证输入的值在数字范围内 |
Optional | 无输入时跳过其他验证函数 |
Required | 确保字段中有数据 |
Regexp | 使用正则表达式验证输入值 |
URL | 验证URL |
AnyOf | 确保输入值在可选值列表中 |
NoneOf | 确保输入值不在可选值列表中 |
把表单渲染成HTML
表单字段是可调用的,在模板中调用后会渲染成HTML。
import
指令允许导入模板中的元素并用在多个模板中。导入的bootstrap/wtf.html
文件中定义了一个使用Bootstrap渲染Flask-WTF表单对象的辅助函数。wtf.quick_from()
函数的参数为Flask-WTF表单对象,使用Bootstrap的默认样式渲染传入的表单。
使用Flask-WTF和Flask-Bootstrap渲染表单
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content%}
Hello,{% if name %}{{name}}{% else %}Stranger{% endif %}!
{{wtf.quick_form(form)}}
{% endblock %}
Jinja2中的条件语句格式为{% if condition %}...{% else %}...{% elseif %}
。
在视图函数中处理表单
路由方法
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
app.route
修饰器中添加的methods
参数告诉Flask在URL映射中把这个视图函数注册为GET和POST请求的处理过程。如果没指定methods
参数,就只把视图函数注册为GET请求的处理程序。
重定向和用户会话
重定向是一种特殊的相应,响应内容是URL,而不是包含HTML代码的字符串。这个技巧称为 POST/重定向/GET模式。
用户对话是一种私有存储,存在于每个连接到服务器的客户端中。用户会话是请求上下文中的变量,名为session
。
默认情况下,用户会话保存在客户端cookie中,使用设置的SECRET_KEY进行加密签名。如果篡改了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'))
redirect()
是个辅助函数,用来生成HTTP重定向响应。redirect()
函数的参数是重定向的URL,这里使用的重定向URL是程序的跟地址。
url_for()
函数使用URL映射生成URL,从而保证URL和定义的路由兼容,而且修改路由名字后依然可用。
url_for()
函数的第一个且唯一必须指定的参数是端点名,即路由的内部名字。路由的端点是相应视图函数的名字。
使用get()
获取字典中键对应的值以避免未找到键的异常情况,因为对于不存在的值,get()
会返回默认值None。
Flash消息
Flash消息
from flask import Flask, render_template, session, redirect, url_for, flash
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
仅调用flash()
函数并不能把消息显示出来,程序使用的模板要渲染这些消息。最好在基模板中渲染Flash消息,因为这样所有页面都能使用这些消息。Flask把get_flashed_messages()
函数开放给模板,用来获取并渲染消息。
渲染Flash消息
{% for message in get_flashed_messages() %}
{{ message}}
{% endfor %}
{% block page_content %}{% endblock %}
在模板中使用循环是因为之前的请求循环中每次调用flash()
函数时都会生成一个消息,所以可能有多个消息在排队等待显示。get_flashed_messages()
函数获取的消息在下次调用时不会再次返回,因此Flash消息只显示一次,然后就消失了。