Flask 入门系列教程(四)

简介: 在 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 安装

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 类字段

字段类 说明 对应的 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 试图函数如下

@app.route('/login/')
def login():
    form = LoginForm()
    return render_template('login.html', form=form)

接着我们再创建一个 login.html 文件,写入如下

{% 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 %}

这样,我们再刷新我们的项目页面,可以看到如下效果

image.png


处理表单数据


一般来说,从获取表单数据到保存表单数据大致需要以下几步:

  • 解析请求,获取表单数据
  • 对数据进行转换,
  • 验证表单数据是否符合要求
  • 如果验证错误,那么提示相关的错误信息
  • 如果验证通过,则保存数据

提交表单

在 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!" % username)
        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 %}
 <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 %}

效果如下:

image.png

文件上传

对于文件上传,其实我们有许多安全的问题需要考虑:

  • 验证文件大小
  • 过滤文件名称
  • 验证文件类型

下面我们来看一看 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 "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 %}

我们重新刷新页面,得到如下效果

image.png

处理上传文件

对于上传的文件,我们在服务器端需要做一定的处理,例如保存、校验等等。

下面我们继续编写 upload 视图函数

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 来获取文件的名称,并保存上传的文件到指定目录

下面就是编写展示图片的视图函数了

@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')


在上传好图片后,我们的程序会跳转至另外的页面,用于展示当前的图片

{% 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 %}


image.png


当然对于表单,还有很多其他的高级应用,比如富文本编辑器等,这些我们留到后面再进行讨论!

这部分的完整代码,可以检出4a


总结


本节我们一起学习了 WEB 表单相关的知识,在后面的学习当中,我们还会多次使用,一定要好好消化这部分哦!

image.png

相关文章
|
4月前
|
安全 网络安全 数据安全/隐私保护
Flask 入门系列教程(六)
Flask 入门系列教程(六)
|
4月前
|
存储 安全 前端开发
Flask 入门系列教程(二)
Flask 入门系列教程(二)
|
2月前
|
人工智能 前端开发 算法
Python 潮流周刊#18:Flask、Streamlit、Polars 的学习教程
Python 潮流周刊#18:Flask、Streamlit、Polars 的学习教程
32 4
|
4月前
|
SQL 关系型数据库 Shell
Flask 入门系列教程(五)
Flask 入门系列教程(五)
|
4月前
|
安全 数据安全/隐私保护 Python
Flask 入门系列教程(四)
Flask 入门系列教程(四)
|
4月前
|
存储 前端开发 JavaScript
Flask 入门系列教程(三)
Flask 入门系列教程(三)
|
4月前
|
Web App开发 前端开发 JavaScript
Flask 入门系列教程(一)
Flask 入门系列教程(一)
|
5月前
|
API 数据库 网络架构
Python Flask框架学习教程
概述: 本教程将介绍Python Flask框架的基础知识和使用方法。Flask是一个简单而灵活的Web应用框架,它具有轻量级、易用性和高度可扩展等特点。通过学习本教程,您将能够了解Flask的基本概念、安装方法、语法结构、模板使用以及实际案例,并通过练习题加深对框架的理解和应用。
|
存储 Python 文件存储
Flask入门request session cookie(二)
1 HTTP方法分类 1 GET 浏览器告知服务器:只获取页面上的信息并发给我。这是最常用的方法。 2 HEAD 浏览器告诉服务器:欲获取信息,但是只关心消息头 。
|
9天前
|
安全 数据库 C++
Python Web框架比较:Django vs Flask vs Pyramid
【4月更文挑战第9天】本文对比了Python三大Web框架Django、Flask和Pyramid。Django功能全面,适合快速开发,但学习曲线较陡;Flask轻量灵活,易于入门,但默认配置简单,需自行添加功能;Pyramid兼顾灵活性和可扩展性,适合不同规模项目,但社区及资源相对较少。选择框架应考虑项目需求和开发者偏好。