使用 WTForms 进行表单验证

当您必须处理浏览器视图提交的表单数据时,代码很快就会变得难以阅读。有一些库旨在使此过程更易于管理。其中之一是 WTForms,我们将在本文中介绍它。如果您发现自己处于需要处理许多表单的情况下,您可能需要尝试一下。

当您使用 WTForms 时,您必须首先将表单定义为类。我建议将应用程序分解为多个模块(作为包的大型应用程序),并为表单添加一个单独的模块。

通过扩展充分利用 WTForms

Flask-WTF 扩展在此模式的基础上进行了扩展,并添加了一些小助手,使表单和 Flask 的使用更加有趣。您可以从 PyPI 获取它。

表单

这是一个典型注册页面的示例表单

from wtforms import Form, BooleanField, StringField, PasswordField, validators

class RegistrationForm(Form):
    username = StringField('Username', [validators.Length(min=4, max=25)])
    email = StringField('Email Address', [validators.Length(min=6, max=35)])
    password = PasswordField('New Password', [
        validators.DataRequired(),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('Repeat Password')
    accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])

在视图中

在视图函数中,此表单的用法如下所示

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User(form.username.data, form.email.data,
                    form.password.data)
        db_session.add(user)
        flash('Thanks for registering')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

请注意,我们在这里暗示视图正在使用 SQLAlchemy(Flask 中的 SQLAlchemy),但这当然不是必需的。根据需要调整代码。

需要记住的事情

  1. 如果数据通过 HTTP POST 方法提交,则从请求 form 值创建表单;如果数据作为 GET 提交,则从请求 args 值创建表单。

  2. 要验证数据,请调用 validate() 方法,如果数据验证成功,该方法将返回 True,否则返回 False

  3. 要访问表单中的单个值,请访问 form.<NAME>.data

模板中的表单

现在来看模板方面。当您将表单传递给模板时,您可以轻松地在其中呈现它们。查看以下示例模板,了解这是多么容易。WTForms 已经为我们完成了一半的表单生成工作。为了使其更美观,我们可以编写一个宏,用于呈现带有标签和错误列表(如果有)的字段。

这是一个示例 _formhelpers.html 模板,其中包含这样的宏

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}

此宏接受几个关键字参数,这些参数将转发到 WTForm 的字段函数,该函数为我们呈现字段。关键字参数将作为 HTML 属性插入。因此,例如,您可以调用 render_field(form.username, class='username') 以向输入元素添加类。请注意,WTForms 返回标准的 Python 字符串,因此我们必须告诉 Jinja2 此数据已使用 |safe 过滤器进行 HTML 转义。

这是我们上面使用的函数的 register.html 模板,它利用了 _formhelpers.html 模板

{% from "_formhelpers.html" import render_field %}
<form method=post>
  <dl>
    {{ render_field(form.username) }}
    {{ render_field(form.email) }}
    {{ render_field(form.password) }}
    {{ render_field(form.confirm) }}
    {{ render_field(form.accept_tos) }}
  </dl>
  <p><input type=submit value=Register>
</form>

有关 WTForms 的更多信息,请访问 WTForms 网站