Flask 入门系列教程(四)

2020-10-10 14:26:49 浏览数 (1)

在 WEB 应用当中,表单是和用户交互的最常见的方式之一,学习好表单,是非常重要的,用户登录注册、撰写文章等等操作都离不开表单的功能。表单的处理并不简单,除了要创建表单,还需要做相关的验证,还有错误提示等等。这些操作如果都从头开始编写,那么就太复杂了,不过幸运的是,我们有强大的 WTForms 帮助我们解决。

HTML 表单

在 HTML 表单中,可以通过 <form> 标签来创建,通过 <input> 来定义字段。

代码语言:javascript复制
<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

验证 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">&times;</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">&times;</button>
     {{ message }}
 </div>
{% endfor %}
{% for message in form.password.errors %}
 <div class="alert alert-warning">
     <button type="button" class="close" data-dismiss="alert">&times;</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">&times;</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">&times;</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 表单相关的知识,在后面的学习当中,我们还会多次使用,一定要好好消化这部分哦!

最后的最后,如果觉得文章给了你一些启发或者帮助,还请帮忙点个赞,给辛苦码字的我一点小小鼓励,谢谢!!

0 人点赞