Flask:处理Web表单
尽管 Flask 的请求对象提供的信息足以处理 Web 表单,但有些任务很单调,而且要重复操作。比如,生成表单的 HTML 代码和验证提交的表单数据。Flask-WTF 扩展可以把处理 Web 表单的过程简化了,这个扩展对独立的 WTForms 包进行了包装,方便集成到 Flask 应用中。
1、配置
Flask-WTF 无须在应用层初始化,但是它要求应用配置一个密钥,Flask 使用这个密钥保护用户会话,防止表单遭到跨站请求伪造(CSRF)攻击。
app.config['SECRET_KEY'] = 'hard to guess string'
2、定义表单
使用 Flask-WTF 时,在服务器端,每个 Web 表单都由一个继承自 FlaskForm
的类表示。
-
字段构造函数的第一个参数是把表单渲染成 HTML 时使用的 label
-
字段对象可附属一个或多个验证函数。验证函数用于验证用户提交的数据是否有效。
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length, Email, Regexp, EqualTo
from wtforms import ValidationError
from ..models import User
class RegistrationForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Length(1, 64),
Email()])
username = StringField('Username', validators=[DataRequired(), Length(1, 64),
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
'Usernames must have only letters, numbers, dots or underscores')])
password = PasswordField('Password', validators=[
DataRequired(), EqualTo('password2', message='Passwords must match.')])
password2 = PasswordField('Confirm password', validators=[DataRequired()])
submit = SubmitField('Register')
def validate_email(self, field):
if User.query.filter_by(email=field.data).first():
raise ValidationError('Email already registered.')
def validate_username(self, field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
WTForms支持的HTML标准字段:
字段类型 | 说明 |
---|---|
BooleanField |
复选框,值为 True 和 False |
DateField |
文本字段,值为 datetime.date 格式 |
DateTimeField |
文本字段,值为 datetime.datetime 格式 |
DecimalField |
文本字段,值为 decimal.Decimal |
FileField |
文件上传字段 |
HiddenField |
隐藏的文本字段 |
MultipleFileField |
多文件上传字段 |
FieldList |
一组指定类型的字段 |
FloatField |
文本字段,值为浮点数 |
FormField |
把一个表单作为字段嵌入另一个表单 |
IntegerField |
文本字段,值为整数 |
PasswordField |
密码文本字段 |
RadioField |
一组单选按钮 |
SelectField |
下拉列表 |
SelectMultipleField |
下拉列表,可选择多个值 |
SubmitField |
表单提交按钮 |
StringField |
文本字段 |
TextAreaField |
多行文本字段 |
WTForms 内建的验证函数:
验证函数 | 说明 |
---|---|
DataRequired |
确保转换类型后字段中有数据 |
Email |
验证电子邮件地址 |
EqualTo |
比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
InputRequired |
确保转换类型前字段中有数据 |
IPAddress |
验证 IPv4 网络地址 |
Length |
验证输入字符串的长度 |
MacAddress |
验证 MAC 地址 |
NumberRange |
验证输入的值在数字范围之内 |
Optional |
允许字段中没有输入,将跳过其他验证函数 |
Regexp |
使用正则表达式验证输入值 |
URL |
验证 URL |
UUID |
验证 UUID |
AnyOf |
确保输入值在一组可能的值中 |
NoneOf |
确保输入值不在一组可能的值中 |
自定义验证方法:如果表单类中定义了以 validate_ 开头且后面跟着字段名的方法,这个方法就和常规的验证函数一起调用
3、渲染表单
视图函数通过 form
参数把一个 NameForm
实例传入模板,在模板中可以生成一个简单的 HTML 表单。还可以为字段指定 id
或 class
属性,然后为其定义 CSS 样式:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(id='my-text-field') }}
{{ form.submit() }}
</form>
使用Flask-Bootstrap渲染
Flask-Bootstrap 扩展提供了一个高层级的辅助函数,可以使用 Bootstrap 预定义的表单样式渲染整个 Flask-WTF 表单。
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
4、处理表单数据
用户提交表单后,服务器会收到一个的 POST
请求。如果能通过验证,validate_on_submit()
返回 True
,用户输入的名字可通过字段的 data
属性获取:
@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)
上述代码能获取表单的数据,但存在一个问题:客户端提交POST请求后,如果刷新页面,会弹出警告。这是因为刷新页面时浏览器会重新发送之前发送过的请求。如果前一个请求是包含表单数据的 POST
请求,刷新页面后会再次提交表单,所以浏览器弹出警告,让用户确认。
鉴于此,最好使用重定向作为POST请求的响应,这样上一个请求就永远是GET。这样的话又产生另一个问题,即重定向的响应无法传入数据,这样就需要使用session来存取数据,所以代码修改如下:
@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'))
默认情况下,session 保存在客户端 cookie 中,使用前面设置的密钥加密签名。如果篡改了 cookie 的内容,签名就会失效,会话也将随之失效。
5、消息闪现
请求完成后,有时需要让用户知道状态发生了变化,可以是确认消息、警告或者错误提醒。Flask 本身内置消息闪现功能,使用方法如下:
① 视图函数中调用flash
函数
视图函数:
...
flash('登陆成功!')
return ...
② 模板中渲染消息:最好在基模版中渲染闪现消息,这样所有页面都可以调用
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
这个示例使用 Bootstrap 提供的 CSS alert
样式渲染警告消息,其他样式可参考相关API文档。