渲染表单模板

WTForms 和 Flask-WTF 提供了将 Python 表单对象渲染成 HTML 表单模板的功能。这里以登录为例,先来实现一个登录的表单类,代码如下。

from wtforms import Form, StringField, BooleanField, SubmitField, ValidationError
from flask_wtf import FlaskForm
from wtforms.validators import Email, Length

class LoginForm(FlaskForm):
    email = StringField(
        label="邮箱:",
        validators=[Email(message="请输入正确格式的邮箱!")],
        render_kw={"placeholder": "请输入邮箱"}
    )
    password = StringField(
        label="密码:",
        validators=[Length(min=6, max=20, message="请输入正确长度的密码!")],
        render_kw={"placeholder": "请输入密码"}
    )
    remember = BooleanField(label="记住我:")
    submit = SubmitField(label="提交")

这里定义了一个 LoginForm 类,并使其继承自 FlaskForm 类。FlaskForm 的父类是 wtforms.Form 类,其在 wtforms.Form 类的基础上增加了一些方便的方法,以使在验证表单数据时,不再需要手动传入 request.form。接着分别定义了 email、password、remember 和 submit 这 4 个字段,这 4 个字段都新增了一个 label 参数,这个参数在渲染表单模板时会为除 submit 以外的字段生成一个 label 标签,因为 submit 是一个提交按钮,在提交按钮中 label 参数会被设置成属性 value 的值。如果在表单标签上设置一些属性,如 placeholder,可以通过参数 render_kw 来实现。

表单类定义好后,就可以在视图函数中使用了,因为现在表单类跟模板深度结合,所以使用方式跟之前有所不同,先看如下示例代码。

from forms import RegisterForm, LoginForm
from flask import Flask, render_template, redirect

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm(meta={"csrf": False})
    if form.validate_on_submit():
        email = form.email.data
        password = form.password.data
        return redirect("/")
    return render_template("login.html", form=form)

上述代码中,先定义了一个 login 视图函数,并使得其同时支持 GET 和 POST 请求。然后创建了一个 LoginForm 对象,并且通过传递 meta 参数,关闭了 CSRF 验证(关于 CSRF, 6.3节会讲到,这里先关闭即可)。我们重点关注渲染模板的代码,这里把 form 对象传给了 login.html 模板,而在 login.html 模板中,现在就可以使用 form 对象来渲染表单元素了,代码如下。

image 2025 01 21 16 33 20 977

上述代码中,首先通过 form.<字段名>.label 渲染了 email、password 和 remember 这 3 个字段的 label 标签,然后通过 form.<字段名> 渲染对应的 input 标签。在每个字段下面循环 form.<字段名>.errors 进行验证,如果验证出现错误,会把所有的错误信息展示出来。然后我们再回过头去看 login 视图函数,重点关注表单验证部分的代码。

def login():
    form = LoginForm(meta={"csrf": False})
    if form.validate_on_submit():
        email = form.email.data
        password = form.password.data
        return redirect("/")
    return render_template("login.html", form=form)

以上代码中,通过调用 form.validate_on_submit() 方法判断是否通过 POST 请求使表单验证成功,如果验证通过,则进入下一步操作,否则依然渲染 login.html 模板。其实此时的 form 上已经保存了验证失败的错误信息,因为在模板中通过 form.<字段名>.errors 已经获取到了对应字段的错误信息。所以如果访问 http://127.0.0.1:5000/login ,不输入任何数据,并直接单击 “提交” 按钮,可以看到如图 6-3 所示的效果。

image 2025 01 21 16 36 24 824
Figure 1. 图6-3 不输入数据直接登录效果

如果输入正确格式的邮箱和密码,再单击“提交”按钮,则会跳转到首页。

关于使用 WTForms 和 Flask-WTF 在模板中渲染表单,个人不太推荐。在 Python 类中定义 HTML 标签及其样式,会提高代码间的耦合度,造成本应在 HTML 模板中完成的工作,却要在 Python 文件中修改,特别是在前后端开发工程师共同开发项目的情况下,这种项目结构的缺点更是异常突出。