在 WEB 应用当中,表单是和用户交互的最常见的方式之一,学习好表单,是非常重要的,用户登录注册、撰写文章等等操作都离不开表单的功能。表单的处理并不简单,除了要创建表单,还需要做相关的验证,还有错误提示等等。这些操作如果都从头开始编写,那么就太复杂了,不过幸运的是,我们有强大的 WTForms 帮助我们解决。
HTML 表单
在 HTML 表单中,可以通过 <form>
标签来创建,通过 <input>
来定义字段。
<form method="post"> <!-- 指定提交方法为 POST -->
<label for="name">用户名</label>
<input type="text" name="name" id="name"><br> <!-- 文本输入框 -->
<label for="occupation">手机号</label>
<input type="text" name="occupation" id="occupation"><br> <!-- 文本输入框 -->
<input type="submit" name="submit" value="登录"> <!-- 提交按钮 -->
</form>
编写表单的 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 安装
代码语言:javascript复制pip install flask-wtf
因为 Flask-WTF 默认会为每一个表单启用 CSRF 保护,Flask-WTF 默认情况下使用程序密钥来对 CSRF 令牌进行签名,所以我们需要进行如下设置
代码语言:javascript复制app.secret = 'my hard secret'
定义 WTForms 表单类
一个表单由若干个输入字段组成,这些字段分别用表单的类属性来表示。下面我们来编写一个登录类
代码语言:javascript复制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 类字段
字段类 | 说明 | 对应的 HTML |
---|---|---|
StringField | 文本字段 | <input type="text"> |
SubmitField | 提交按钮 | <input type="submit"> |
PasswordField | 密码文本字段 | <input type="password"> |
FileField | 文件上传字段 | <input type="file"> |
SelectField | 下拉列表 | <select></select> |
在 WTForms 中,验证器(validator)是一系列用于验证字段数据的类,我们在实例化字段类时使用 validators 关键字来指定附加验证器列表。
如下是常用的验证器
验证器 | 说明 |
---|---|
DataRequired | 验证数据是否存在 |
验证 email 地址 | |
EqualTo | 验证两个字段是否一致 |
在模板中渲染表单
为了能够在模板中渲染表单,我们需要把表单实例传入模板。首先实例化表单类 LoginForm,然后在 render_template() 函数中传入模板,于是我们修改 login 试图函数如下
代码语言:javascript复制@app.route('/login/')
def login():
form = LoginForm()
return render_template('login.html', form=form)
接着我们再创建一个 login.html 文件,写入如下
代码语言:javascript复制{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Login{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}
这样,我们再刷新我们的项目页面,可以看到如下效果
处理表单数据
一般来说,从获取表单数据到保存表单数据大致需要以下几步:
- 解析请求,获取表单数据
- 对数据进行转换,
- 验证表单数据是否符合要求
- 如果验证错误,那么提示相关的错误信息
- 如果验证通过,则保存数据
提交表单
在 HTML 中,当表单类型为 submit 的字段被点击时,就会创建一个提交表单的 HTTP 请求,请求中会包含表单中的各个字段。
由于 Flask 为路由默认设置的监听的 HTTP 请求为 GET,而表单往往都是 POST 请求,所以我们需要手动给试图函数绑定 POST 请求
代码语言:javascript复制@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
return render_template('login.html', form=form)
在试图函数中处理表单
对于数据的验证,我们可以使用函数 validate_on_submit(),如果返回 True,则代表验证通过。
代码语言:javascript复制@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
session['username'] = username
flash("登录成功,%s!" % username)
return redirect(url_for('index'))
return render_template('login.html', form=form)
在这里,我们通过 form.username.data 来获取表单中的用户名,并通过 session 来保存,然后再重定向到 index 视图函数 下面我们再来看看 index 视视图函数
代码语言:javascript复制@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 中添加如下代码
代码语言:javascript复制{% for message in form.username.errors %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% for message in form.password.errors %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
效果如下:
文件上传
对于文件上传,其实我们有许多安全的问题需要考虑:
- 验证文件大小
- 过滤文件名称
- 验证文件类型
下面我们来看一看 WTForms 能帮助我们做些什么
首先定义一个文件上传的表单类,一个图片上传的表单
代码语言:javascript复制class UploadForm(FlaskForm):
photo = FileField('Upload Image', validators=[file_required(), file_allowed(upload_set='.jpg')])
submit = SubmitField('Upload')
在这里,我们定义了用于上传文件的表单,并且限制了只能上传 jpg 格式的文件类型 下面我们编写上传图片的视图函数 upload
代码语言:javascript复制@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
return render_template('upload.html', form=form)
这里其实与登录的视图函数是类似的写法 接下来就是 upload.html 文件的编写
代码语言:javascript复制{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Upload{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}
我们重新刷新页面,得到如下效果
处理上传文件
对于上传的文件,我们在服务器端需要做一定的处理,例如保存、校验等等。
下面我们继续编写 upload 视图函数
代码语言:javascript复制app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads')
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
f = form.photo.data
filename = f.filename
f.save(os.path.join(app.config['ULOAD_PATH'], filename))
flash('上传图片文件成功!')
session['filename'] = filename
return redirect(url_for('show_images'))
return render_template('upload.html', form=form)
我们通过 f.filename 来获取文件的名称,并保存上传的文件到指定目录 下面就是编写展示图片的视图函数了
代码语言:javascript复制@app.route('/uploads/<path:filename>')
def get_file(filename):
return send_from_directory(app.config['UPLOAD_PATH'], filename)
@app.route('/uploaded-images')
def show_images():
return render_template('uploaded.html')
在上传好图片后,我们的程序会跳转至另外的页面,用于展示当前的图片
代码语言:javascript复制{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Upload{% endblock %}
{% block page_content %}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% if session.filename %}
<a href="{{ url_for('get_file', filename=session.filename) }}" target="_blank">
<img src="{{ url_for('get_file', filename=session.filename) }}">
</a>
{% endif %}
{% endblock %}
当然对于表单,还有很多其他的高级应用,比如富文本编辑器等,这些我们留到后面再进行讨论!
这部分的完整代码,可以检出4a
总结
本节我们一起学习了 WEB 表单相关的知识,在后面的学习当中,我们还会多次使用,一定要好好消化这部分哦!
最后的最后,如果觉得文章给了你一些启发或者帮助,还请帮忙点个赞,给辛苦码字的我一点小小鼓励,谢谢!!