04.flask表单

配套视频教程

本文B站配套视频教程

Flask-WTF 扩展可以把处理 Web 表单的过程变成一种愉悦的体验。这个扩展对独立的 WTForms 包进行了包装,方便集成到 Flask 应用中。

Flask-WTF 及其依赖可使用 pip 安装:

pip install flask-wtf

配置

与其他多数扩展不同,Flask-WTF 无须在应用层初始化,但是它要求应用配置一个密钥。密钥是一个由随机字符构成的唯一字符串,通过加密或签名以不同的方式提升应用的安全性。Flask 使用这个密钥保护用户会话,以防被篡改。

hello.py

app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'

app.config 字典可用于存储 Flask、扩展和应用自身的配置变量。使用标准的字典句法就能把配置添加到 app.config 对象中。这个对象还提供了一些方法,可以从文件或环境中导入配置。

Flask-WTF 之所以要求应用配置一个密钥,是为了防止表单遭到跨站请求伪造(CSRF,cross-site request forgery)攻击。恶意网站把请求发送到被攻击者已登录的其他网站时,就会引发 CSRF 攻击。Flask-WTF 为所有表单生成安全令牌,存储在用户会话中。

表单类

使用 Flask-WTF 时,在服务器端,每个 Web 表单都由一个继承自 FlaskForm 的类表示。这个类定义表单中的一组字段,每个字段都用对象表示。字段对象可附属一个或多个验证函数。验证函数用于验证用户提交的数据是否有效。

示例是一个简单的 Web 表单,包含一个文本字段和一个提交按钮。

hello.py

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')

这个表单中的字段都定义为类变量,而各个类变量的值是相应字段类型的对象。在这个示例中,NameForm 表单中有一个名为 name 的文本字段和一个名为 submit 的提交按钮。StringField 类表示属性为 type="text" 的 HTML <input> 元素。SubmitField 类表示属性为 type="submit" 的 HTML <input> 元素。字段构造函数的第一个参数是把表单渲染成 HTML 时使用的标注(label)。

StringField 构造函数中的可选参数 validators 指定一个由验证函数组成的列表,在接受用户提交的数据之前验证数据。验证函数 DataRequired() 确保提交的字段内容不为空。

FlaskForm 基类由 Flask-WTF 扩展定义,所以要从 flask_wtf 中导入。然而,字段和验证函数却是直接从 WTForms 包中导入的。

WTForms 支持的 HTML 标准字段如表所示。

表:WTForms支持的HTML标准字段

字段类型 说明
BooleanField 复选框,值为 TrueFalse
DateField 文本字段,值为 datetime.date 格式
DateTimeField 文本字段,值为 datetime.datetime 格式
DecimalField 文本字段,值为 decimal.Decimal
FileField 文件上传字段
HiddenField 隐藏的文本字段
MultipleFileField 多文件上传字段
FieldList 一组指定类型的字段
FloatField 文本字段,值为浮点数
FormField 把一个表单作为字段嵌入另一个表单
IntegerField 文本字段,值为整数
PasswordField 密码文本字段
RadioField 一组单选按钮
SelectField 下拉列表
SelectMultipleField 下拉列表,可选择多个值
SubmitField 表单提交按钮
StringField 文本字段
TextAreaField 多行文本字段

WTForms 内建的验证函数如表所示。

表:WTForms验证函数

验证函数 说明
DataRequired 确保转换类型后字段中有数据
Email 验证电子邮件地址
EqualTo 比较两个字段的值;常用于要求输入两次密码进行确认的情况
InputRequired 确保转换类型前字段中有数据
IPAddress 验证 IPv4 网络地址
Length 验证输入字符串的长度
MacAddress 验证 MAC 地址
NumberRange 验证输入的值在数字范围之内
Optional 允许字段中没有输入,将跳过其他验证函数
Regexp 使用正则表达式验证输入值
URL 验证 URL
UUID 验证 UUID
AnyOf 确保输入值在一组可能的值中
NoneOf 确保输入值不在一组可能的值中

把表单渲染成HTML

表单字段是可调用的,在模板中调用后会渲染成 HTML。假设视图函数通过 form 参数把一个 NameForm 实例传入模板,在模板中可以生成一个简单的 HTML 表单,如下所示:

<form method="POST">
    {{ form.hidden_tag() }}
    {{ form.name.label }} {{ form.name() }}
    {{ form.submit() }}
</form>

注意,除了 namesubmit 字段,这个表单还有个 form.hidden_tag() 元素。这个元素生成一个隐藏的字段,供 Flask-WTF 的 CSRF 防护机制使用。

当然,这种方式渲染出的表单还很简陋。调用字段时传入的任何关键字参数都将转换成字段的 HTML 属性。例如,可以为字段指定 idclass 属性,然后为其定义 CSS 样式:

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

即便能指定 HTML 属性,但按照这种方式渲染及美化表单的工作量还是很大,所以在条件允许的情况下,最好使用 Bootstrap 的表单样式。Flask-Bootstrap 扩展提供了一个高层级的辅助函数,可以使用 Bootstrap 预定义的表单样式渲染整个 Flask-WTF 表单,而这些操作只需一次调用即可完成。使用 Flask-Bootstrap,上述表单可以用下面的方式渲染:

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

import 指令的使用方法和普通 Python 代码一样,通过它可以导入模板元素,在多个模板中使用。导入的 bootstrap/wtf.html 文件中定义了一个使用 Bootstrap 渲染 Flask-WTF 表单对象的辅助函数。wtf.quick_form() 函数的参数为 Flask-WTF 表单对象,使用 Bootstrap 的默认样式渲染传入的表单。hello.py 的完整模板如示例所示。

示例 templates/index.html:使用 Flask-WTF 和 Flask-Bootstrap 渲染表单

{% 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 %}

base.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle"
             data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
<div class="container">
    {% block page_content %}{% endblock %}
</div>
{% endblock %}

在视图函数中处理表单

在新版 hello.py 中,视图函数 index() 有两个任务:一是渲染表单,二是接收用户在表单中填写的数据。以下示例是更新后的 index() 视图函数。

示例 hello.py:使用 GETPOST 请求方法处理 Web 表单

@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 映射中把这个视图函数注册为 GETPOST 请求的处理程序。如果没指定 methods 参数,则只把视图函数注册为 GET 请求的处理程序。

这里有必要把 POST 加入方法列表,因为更常使用 POST 请求处理表单提交。表单也可以通过 GET 请求提交,但是 GET 请求没有主体,提交的数据以查询字符串的形式附加到 URL 中,在浏览器的地址栏中可见。基于这个以及其他多个原因,处理表单提交几乎都使用 POST 请求。

图1 是用户首次访问网站时浏览器显示的表单。用户提交名字后,应用会生成一个针对该用户的欢迎消息。欢迎消息下方还是会显示这个表单,以便用户输入新名字。图 2 显示了此时应用的样子。

image.png

图1:Flask-WTF Web 表单

image.png

图2:提交后显示的 Web 表单

如果用户提交表单之前没有输入名字,那么 DataRequired() 验证函数会捕获这个错误,如图3 所示。注意这个扩展自动提供了多少功能。这说明,像 Flask-WTF 和 Flask-Bootstrap 这样设计良好的扩展能给应用提供十分强大的功能。

image.png

图3:验证失败后显示的 Web 表单

posted @ 2020-10-21 13:49  [豆约翰]  阅读(2058)  评论(0编辑  收藏  举报