日課書

编程100小时挑战

Flask中使用Web表单

在Flask中通过request.form以获取Web表单数据。但是生成表单的HTML代码和验证提交表单的数据,麻烦而且要重复操作。Flask-WTF扩展对WTForms包进行了包装,可以简化Flask中表单的处理。

1. 跨站请求伪造保护

WTF扩展可以使用配置中的通用密钥,生成加密令牌,再用令牌验证求情中表单数据的真伪。通常为了增加安全性,密钥不应直接写入代码,而要保存在环境变量中。

1
2
3
4
5
6
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

# get SECRET_KEY from environment
import os
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'

2. 表单类

在使用Flask-WTF时,每个Web表单都由一个继承自Form的类表示。这个类定义了表单的字段,和本字段的验证方法,验证方法用来验证用户提交的输入值是否符合要求。

1
2
3
4
5
6
7
from flask_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')

3. 把表单渲染成HTML

1
2
3
4
5
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(id='my-text-field') }}
{{ form.submit() }}
</form>

这样渲染表单依旧很麻烦,而在Flask-Bootstrap中有预先定义好的样式,可以直接渲染整个Flask-WTF表单。

1
2
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}

4. 在视图函数中处理表单

视图函数中需要渲染表单,接收处理表单数据。

1
2
3
4
5
6
7
8
@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)

5. 重定向和用户会话

以上方法构建的页面有一个问题。在刷新提交过表单的页面后,会有一个再次调教表单的确认信息。这是因为,刷新后浏览器会自动提交最后一次请求,即之前的POST请求。它影响了使用体验,同时也让不了解的用户产生疑惑。一个解决办法就是在完成POST请求后重定向,再发送一个GET请求。

但是在之后的GET请求中我们就无法获取POST的表单数据。此时可以利用用户会话Session,将数据加密存储于客户端的cookie中,方便程序的请求间「记住」数据。

1
2
3
4
5
6
7
8
9
10
from flask import render_template, redirect, session, 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'))