Hello, Flask!
首发于Hello, Flask!
Flask表单:表单的创建与渲染

Flask表单:表单的创建与渲染

使用Flask-WTF创建和渲染表单

Flask-WTF是一个集成了WTForms的Flask扩展,使用它你可以在python文件里创建表单类,然后在HTML使用它提供的函数渲染表单。


跨站请求伪造保护

Flask-WTF默认支持CSRF(跨站请求伪造)保护,只需要在程序中设置一个密钥。Flask-WTF使用这个密钥生成加密令牌,再用令牌验证表单中数据的真伪。(关于Flask项目的配置和初始化,后续文章会谈到)你可以使用app.config字典来存储配置变量:

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

或是从系统环境变量中获取

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')

很多重要的信息,比如邮箱账户和密码都不能直接写在程序里,而要设置系统环境变量,设置方法如下(Windows):

set SECRET_KEY=very very hard to guess string
*Unix用户使用export替换set。
注意,后面的字符串不需要用任何符号括起来。
创建表单

然后我们就可以开始创建表单了,以一个简单的登录表单为例,首先是导入:

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, Length, Email
从Flask-WTF导入Form类,而表单字段和验证函数直接从WTForms导入。

每个表单都用一个继承自Form的类表示,每个字段都用一个对象表示,每个对象可以附加多个验证函数。常见的验证函数有Required(),Length(),Email()等。

在Flask-WTF 0.13版本,引入的表单类为FlaskForm;
在WTForms 3.0版本,验证函数Required变为DataRequired

然后是我们的表单类:

class LoginForm(FlaskForm):
    email = StringField(u'邮箱', validators=[
                DataRequired(message= u'邮箱不能为空'), Length(1, 64),
                Email(message= u'请输入有效的邮箱地址,比如:username@domain.com')])
    password = PasswordField(u'密码', 
                  validators=[Required(message= u'密码不能为空')])
    submit = SubmitField(u'登录')
在验证函数中传入出错时的提示信息,覆盖默认的英文错误提示。

参考链接:

表单渲染

首先需要在视图函数里引入一个表单类的实例,然后在返回模板的时候,传入这个实例:

@auth.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        ...
    return render_template('login.html', form=form)

在HTML文件(login.html)里,我们使用下面的方式来渲染表单:

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

这种方式渲染出来的表单没有样式(字段后面的括号里可以填入input属性,如果不填可以省略掉括号,控制表单样式内容见续篇《自定义表单样式》),而且比较繁琐,你也可以使用for循环来遍历字段。

关于form.hidden_tag()

上面说了,Flask-WTF支持跨站请求伪造保护,所以它会为你的表单类自动创建一个CSRF字段,你需要在表单里渲染这个字段:{{ form.csrf_token }}。

而如果你有多个隐藏字段(type属性为hidden)的话,就可以使用{{ form.hidden_tag() }}来渲染所有隐藏字段。


使用Flask-Bootstrap渲染表单

Flask-Bootstrap是一个集成了Bootstrap的Flask扩展,它提供了一些表单渲染的函数,可以让我们方便的渲染Bootstrap样式的表单,类似这样:

要使用这些函数,首先你要安装Flask-WTF,然后在HTML文件开头引入提供这些函数的模板文件(wtf.html):

{% import 'bootstrap/wtf.html' as wtf %}

使用Flask-Bootstrap提供的wtf.form_field()函数来渲染表单:

{% import "bootstrap/wtf.html" as wtf %}
<form class="form" method="POST">
    {{ form.hidden_tag() }}
    {{ wtf.form_field(form.title) }}
    {{ wtf.form_field(form.body) }}
    {{ wtf.form_field(form.submit) }}
</form>

假如你不需要调整Boostrap的默认表单样式,只想要快速生成表单,那么Flask-Bootstrap还提供了一个强大的函数,只需要一行就可以快速生成表单:

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

纯HTML表单或混合

你可以把你想定义的字段与Flask-WTF的渲染字段混在一起,比如像这样直接将确认按钮写在模板里:

<form method="POST">
    {{ form.csrf_token }}
    {{ form.name.label }} {{ form.name(size=20) }}
    <input type="submit" value="确认">
</form>
注:在视图函数里获取数据时,Flask-WTF不验证是否按下了确认按钮,只是判断HTTP方法。

你也可以不使用Flask-WTF,直接把表单写在HTML模板里。这在一些特殊情况下,反而会比使用Flask-WTF更加方便。比如在小项目里,或是需要高度自定义的情况下。在上一个Flask实践项目计时器里,传入时间参数的表单就是直接写在HTML里的:


<form method="POST" action="{{ url_for('custom') }}">
    <input type="text" name="time" class="time-input" placeholder="example: 12/30s/20m/2h">
    <input type="submit" class="startButton" value="START">
</form>

需要注意的是,这时要给表单添加一个action属性,属性值填写要处理表单数据的视图函数。下一篇文章会详细谈到怎么处理表单的数据。

Flask扩展一般通过安装pip安装(注意要安装在虚拟环境,别忘了激活虚拟环境后再安装):
pip install flask-wtf
注意导入时名称的变化,《Flask Web开发》中使用flask.ext.extension_name导入扩展的方式被废弃,使用下面的方式(flask_extension_name)导入扩展中的函数或类:
from flask_wtf import FlaskForm

*考虑写一篇文章讲一下扩展的安装、初始化和配置


参考链接

  1. Cross-site request forgery
  2. Flask-WTF - Flask-WTF 0.13
  3. WTForms Documentation
  4. flask.pocoo.org/docs/0.
  5. flask.pocoo.org/snippet

- - - - -

更多关于Flask的优质内容,欢迎关注Hello, Flask! - 知乎专栏

编辑于 2016-11-15

文章被以下专栏收录

    大家好,我是李辉,一个 Python 程序员。在这个专栏,你会看到我学习和使用 Flask 的经验和总结,你还会看到我所有的发明创造,以及它们的实现方法。欢迎加入这场 Flask 之旅,拿好你的小键盘,上车吧!helloflask.com