在 WEB 应用当中,表单是和用户交互的最常见的方式之一,学习好表单,是非常重要的,用户登录注册、撰写文章等等操作都离不开表单的功能。表单的处理并不简单,除了要创建表单,还需要做相关的验证,还有错误提示等等。这些操作如果都从头开始编写,那么就太复杂了,不过幸运的是,我们有强大的 WTForms 帮助我们解决。 HTML 表单
在 HTML 表单中,可以通过
编写表单的 HTML 代码有下面几点需要注意:
? 在form标签里使用method属性将提交表单数据的 HTTP 请求方法指定为 POST。如果不指定,则会默认使用 GET 方法,这会将表单数据通过 URL 提交,容易导致数据泄露,而且不适用于包含大量数据的情况。
? 对于input元素必须要指定name属性,否则无法提交数据,在服务器端,我们也需要通过这个name属性值来获取对应字段的数据。
当然,编写 HTML 代码并不是我们的主要工作,所以我们可以通过 Flask 的相关插件来自动生成这部分 HTML 代码。
WTForms
WTForms 支持在 Python 中使用类定义表单,然后直接通过类定义生成对应的 HTML 代码,这种方式更加方便,而且也更易于重用。因此,在一般的情况下,我们都不会直接使用 HTML 编写表单,使用 WTForms 是我们的第一选择。
使用 Flask-WTF 处理表单
扩展 Flask-WTF 集成了 WTForms,使用它可以在 Flask 中方便的使用 WTForms。Flask-WTF 将帮助我们更加方便的处理表单,包括表单的生成、解析、CSRF等等。
安装 Flask-WTF 还是一样的,直接通过 pip 安装 pip install flask-wtf
因为 Flask-WTF 默认会为每一个表单启用 CSRF 保护,Flask-WTF 默认情况下使用程序密钥来对 CSRF 令牌进行签名,所以我们需要进行如下设置 app.secret = 'my hard secret' 定义 WTForms 表单类
一个表单由若干个输入字段组成,这些字段分别用表单的类属性来表示。下面我们来编写一个登录类
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectMultipleField, SelectField
from wtforms.validators import DataRequired, EqualTo, ValidationError class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()]) submit = SubmitField('Login') 我们定义了一个 LoginForm 类,该类中又定义了三个字段,就是后面我们在 web 页面上会看到的表单字段。 如下是一些常用的 WTForms 类字段 字段类 StringField 说明 文本字段 提交按钮 对应的 HTML 在 WTForms 中,验证器(validator)是一系列用于验证字段数据的类,我们在实例化字段类时使用 validators 关键字来指定附加验证器列表。 如下是常用的验证器 验证器 DataRequired Email EqualTo 说明 验证数据是否存在 验证 email 地址 验证两个字段是否一致 在模板中渲染表单 为了能够在模板中渲染表单,我们需要把表单实例传入模板。首先实例化表单类 LoginForm,然后在 render_template() 函数中传入模板,于是我们修改 login 试图函数如下 @app.route('/login/') def login(): form = LoginForm() return render_template('login.html', form=form) 接着我们再创建一个 login.html 文件,写入如下
{% extends \
{% import \
{% block title %}My Web - Login{% endblock %}
{% block page_content %} {{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
{% endfor %} {% endblock %}
这样,我们再刷新我们的项目页面,可以看到如下效果
? ? ? ? ?
处理表单数据
一般来说,从获取表单数据到保存表单数据大致需要以下几步: 解析请求,获取表单数据 对数据进行转换,
验证表单数据是否符合要求
如果验证错误,那么提示相关的错误信息 如果验证通过,则保存数据 提交表单
在 HTML 中,当表单类型为 submit 的字段被点击时,就会创建一个提交表单的 HTTP 请求,请求中会包含表单中的各个字段。
由于 Flask 为路由默认设置的监听的 HTTP 请求为 GET,而表单往往都是 POST 请求,所以我们需要手动给试图函数绑定 POST 请求 @app.route('/login/', methods=['GET', 'POST']) def login():
form = LoginForm()
return render_template('login.html', form=form)
在试图函数中处理表单
对于数据的验证,我们可以使用函数 validate_on_submit(),如果返回 True,则代表验证通过。
@app.route('/login/', methods=['GET', 'POST']) def login():
form = LoginForm()
if form.validate_on_submit(): username = form.username.data session['username'] = username
flash(\登录成功,%s!\ return redirect(url_for('index'))
return render_template('login.html', form=form)
在这里,我们通过 form.username.data 来获取表单中的用户名,并通过 session 来保存,然后再重定向到 index 视图函数 下面我们再来看看 index 视视图函数 @app.route('/') def index():
user = session.get('username')
return render_template('index.html', user=user)
这个就相对简单了,从 session 中拿到用户名,然后传递给 index.html 模板,而 index.html 模板则与前面我们做的类似,就不再赘述了。
进阶应用
在模板中渲染错误
如果函数 validate_on_submit() 返回 false,那么说明表单提交的数据验证不通过,WTForms 会把错误消息添加到表单类的 error 属性中,我们可以在模板中轻松的取出。
在 loging.html 中添加如下代码
{% for message in form.username.errors %}
{% endfor %}
{% for message in form.password.errors %}
{% endfor %}
效果如下:
文件上传
对于文件上传,其实我们有许多安全的问题需要考虑: ? 验证文件大小 ? 过滤文件名称 ? 验证文件类型
下面我们来看一看 WTForms 能帮助我们做些什么
首先定义一个文件上传的表单类,一个图片上传的表单 class UploadForm(FlaskForm):
photo = FileField('Upload Image', validators=[file_required(), file_allowed(upload_set='.jpg')])
submit = SubmitField('Upload')
在这里,我们定义了用于上传文件的表单,并且限制了只能上传 jpg 格式的文件类型
下面我们编写上传图片的视图函数 upload @app.route('/upload', methods=['GET', 'POST']) def upload():
form = UploadForm()
return render_template('upload.html', form=form)
这里其实与登录的视图函数是类似的写法 接下来就是 upload.html 文件的编写 {% extends \
{% import \
{% block title %}My Web - Upload{% endblock %}
{% block page_content %} {{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
{% endfor %}
{% endblock %}
我们重新刷新页面,得到如下效果
处理上传文件
对于上传的文件,我们在服务器端需要做一定的处理,例如保存、校验等等。
下面我们继续编写 upload 视图函数
app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads') @app.route('/upload', methods=['GET', 'POST']) def upload():
form = UploadForm()
相关推荐: