在Flask中,通常使用Flask-WTF扩展来对Web表单进行处理,这样更加方便。首先在虚拟环境中安装此扩展:
pip install flask-wtf
Flask-WTF扩展不用在应用层初始化,但是需要配置一个密钥(SECRET_KEY),Flask 使用这个密钥保护用户会话,以防被篡改,也就是防止 CSRF 攻击。
app = Flask(_name__)
app.config['SECRET_KEY'] = 'I am Lethe'
app.config 字典可用于存储 Flask、扩展和应用自身的配置变量。实际上为了增强安全性,密钥不应该直接写入源码,而要保存在环境变量中。
使用Flask-WTF时,服务端的每个Web表单都由一个继承自 FlaskForm 的类表示。这个类定义表单中的一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数 。验证函数用于验证用户提交的数据是否有效。
下面的表单示例中包含一个文本字段和一个提交按钮:
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class NameForm(FlaskForm):
name = StringField('What is your name?', validator=[DataRequired()])
submit = SubmitField('Submit')
上面代码中:
type="text"
HTML
元素。type="submit"
的HTML元素
元素。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 | 确保转换类型后字段中有数据 |
验证电子邮件地址 | |
EqualTo | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
InputRequired | 确保转换类型前字段中有数据 |
IPAddress | 验证 IPv4 网络地址 |
Length | 验证输入字符串的长度 |
MacAddress | 验证 MAC 地址 |
NumberRange | 验证输入的值在数字范围之内 |
Optional | 允许字段中没有输入,将跳过其他验证函数 |
Regexp | 使用正则表达式验证输入值 |
URL | 验证 URL |
UUID | 验证 UUID |
AnyOf | 确保输入值在一组可能的值中 |
NoneOf | 确保输入值不在一组可能的值中 |
表单字段是可调用的,在模板中调用后会渲染成HTML。假设视图函数通过 form
参数把上面的 NameForm 实例传入模板,在模板中可以生成一个简单的HTML表单,如下:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
form>
这里的 form.hidden_tag()
元素生成一个隐藏的字段,用于 CSRF 防护。
在调用字段的时候,传入的关键词参数将转换为字段的HTML属性。 如下,指定id或class属性,从而可以定义CSS样式:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(id='my-text-field') }}
{{ form.submit() }}
form>
在实际渲染及美化表单时,可以直接使用 Bootstrap 的表单样式,通过Flask-Bootstrap扩展,如下:
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(from) }}
使用 import 导入 bootstrap/wtf.html
文件,其中定义了一个用默认Bootstrap 样式渲染的 Flask-WTF 表单对象的辅助函数 wtf.quick_form()
,参数为Flask-WTF 表单对象。
使用Flask-WTF 和 Flask-Bootstrap渲染表单,templates/index.html:
{% 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 %}
定义了表单的类,需要在视图函数中对表单进行处理和渲染,下例使用 GET 和 POST 方法处理表单:
hello.py
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data # 将表单中name字段值赋给name
form.name.data = '' # 清空表单
return render_template('index.html', form=form, name=name)
validate_on_submit()
方法:提交表单后,数据能被所有验证函数接收,返回True;否则返回False。render_template()
渲染表单时,将变量name,和对象form传入到模板中。在实际情况中,最好别让Web应用把 POST 请求作为浏览器发送的最后一个请求,因此可以使用重定向作为POST请求的响应。
如在用POST方式提交完表单后,再向重定向的URL发送GET请求,显示页面的内容,用户不会察觉到有所不同,这叫做 “POST / 重定向 / GET” 模式。
在这种模式下,应用处理POST请求时,需要用变量保存输入的数据,否则一旦重定向后,就无法再通过form.name.data获取POST请求中的字段值。
应用也可以把数据存储在用户会话(session)中,以便在请求之间“记住”数据。用户会话是一种私有存储,每个连接到服务器的客户端都可访问。
改进后的hello.py:
from flask import Flask, render_template, session, redirect, url_for
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY'] = 'I am Lethe'
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@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中通过 flash()
函数实现此功能:
from flask import Flask, render_template, session, redirect, url_for, flash
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_moment import Moment
app = Flask(__name__)
bootstrap = Bootstrap(app)
moment = Moment(app)
app.config['SECRET_KEY'] = 'I am Lethe'
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@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'))
Flask中用 get_flashed_message()
函数在模板中获取并渲染闪现消息,如下修改emplates/base.html中的 content 块:
{% block content %}
<div class="container">
{% for message in get_flashed_message() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×button>
{{ message }}
div>
{% endfor %}
{% block page_content %}{% endblock %}
div>
{% endblock %}
两次提交不同的值,效果如下: