如何使用Flask-WTF使用和验证Web表单
作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。
介绍
Web 表单(例如文本字段和文本区域)使用户能够向您的应用程序发送数据,无论是应用程序用来执行操作的下拉菜单或单选按钮,还是发送大面积文本到被处理或显示。 例如,在社交媒体应用程序中,您可能会为用户提供一个框,他们可以在其中向页面添加新内容。
Flask 是一个轻量级的 Python Web 框架,它为使用 Python 语言创建 Web 应用程序提供了有用的工具和功能。 要在 Flask 中以安全且灵活的方式呈现和验证 Web 表单,您将使用 Flask-WTF,它是一个 Flask 扩展,可帮助您在 Flask 中使用 WTForms 库应用。
WTForms 是一个 Python 库,提供灵活的 Web 表单渲染。 您可以使用它来呈现文本字段、文本区域、密码字段、单选按钮等。 WTForms 还使用不同的 验证器 提供强大的数据验证,验证用户提交的数据是否符合您定义的某些标准。 例如,如果您有一个必填字段,您可以确保用户提交的数据是提供的,或者具有一定的长度。
WTForms 还使用 CSRF 令牌来防止 CSRF 攻击 ,这些攻击允许攻击者在用户经过身份验证的 Web 应用程序上执行不需要的操作。 成功的 CSRF 攻击可以迫使用户执行状态更改请求,例如将资金转移到银行应用程序中攻击者的银行帐户、更改用户的电子邮件地址等。 如果受害者是管理帐户,CSRF 可能会破坏整个 Web 应用程序。
在本教程中,您将构建一个小型 Web 应用程序,演示如何使用 Flask-WTF 呈现和验证 Web 表单。 该应用程序将有一个用于显示存储在 Python 列表中的课程的页面,并且索引页面将有一个用于输入课程名称、其描述、价格、可用性和级别(初级、中级或高级)的表单。
先决条件
- 本地 Python 3 编程环境。 按照 如何为 Python 3 系列安装和设置本地编程环境中的操作系统教程进行操作。 在本教程中,您将调用项目目录
flask_app
。 - 了解基本的 Flask 概念,例如路由、视图函数和模板。 如果您不熟悉 Flask,请查看 如何使用 Flask 和 Python 创建您的第一个 Web 应用程序 和 如何在 Flask 应用程序中使用模板。
- 了解基本的 HTML 概念。 您可以查看我们的 如何使用 HTML 构建网站系列教程了解背景知识。
- (optional) 了解 Flask 中的基本 Web 表单用法。 请参阅 如何在 Flask 应用程序中使用 Web 表单。
第 1 步 — 安装 Flask 和 Flask-WTF
在这一步中,您将安装 Flask 和 Flask-WTF,它还会自动安装 WTForms 库。
激活虚拟环境后,使用 pip
安装 Flask 和 Flask-WTF:
pip install Flask Flask-WTF
安装成功完成后,您将在输出末尾看到类似于以下内容的行:
OutputSuccessfully installed Flask-2.0.2 Flask-WTF-1.0.0 Jinja2-3.0.3 MarkupSafe-2.0.1 WTForms-3.0.0 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1
如您所见,WTForms 库也作为 Flask-WTF 包的依赖项安装。 其余的包是 Flask 依赖项。
现在您已经安装了所需的 Python 包,接下来您将设置一个 Web 表单。
第 2 步 — 设置表格
在此步骤中,您将使用将从 WTForms 库导入的字段和验证器来设置 Web 表单。
您将设置以下字段:
- Title: 课程标题的文本输入字段。
- Description: 课程描述的文本区域。
- Price: 课程价格的整数字段。
- Level: 课程级别的单选字段,具有三个选项:初级、中级和高级。
- Available: 一个复选框字段,指示课程当前是否可用。
首先,在您的 flask_app
目录中打开一个名为 forms.py
的新文件。 该文件将包含您在应用程序中需要的表格:
nano forms.py
该文件将有一个代表您的 Web 表单的类。 在顶部添加以下导入:
烧瓶应用程序/forms.py
from flask_wtf import FlaskForm from wtforms import (StringField, TextAreaField, IntegerField, BooleanField, RadioField) from wtforms.validators import InputRequired, Length
要构建 Web 表单,您将创建从 flask_wtf
包导入的 FlaskForm
基类的子类。 您还需要指定您在表单中使用的字段,您将从 wtforms
包中导入这些字段。
您从 WTForms 库中导入以下 字段 :
- StringField:文本输入。
- TextAreaField:文本区域字段。
- IntegerField:整数字段。
- BooleanField:复选框字段。
- RadioField:用于显示单选按钮列表供用户选择的字段。
在 from wtforms.validators import InputRequired, Length
行中,您导入验证器以在字段上使用以确保用户提交有效数据。 InputRequired 是一个验证器,您将使用它来确保提供输入,而 Length 用于验证字符串的长度以确保它具有最少的字符数,或者它不超过一定长度。
接下来,在 import
语句之后添加以下 class:
烧瓶应用程序/forms.py
class CourseForm(FlaskForm): title = StringField('Title', validators=[InputRequired(), Length(min=10, max=100)]) description = TextAreaField('Course Description', validators=[InputRequired(), Length(max=200)]) price = IntegerField('Price', validators=[InputRequired()]) level = RadioField('Level', choices=['Beginner', 'Intermediate', 'Advanced'], validators=[InputRequired()]) available = BooleanField('Available', default='checked')
保存并关闭文件。
在这个 CourseForm
类中,您继承自之前导入的 FlaskForm
基类。 您可以使用从 WTForms 库导入的表单字段将一组表单字段定义为类变量。 当您实例化一个字段时,第一个参数是该字段的标签。
您可以通过传递从 wtforms.validators
模块导入的验证器列表来定义每个字段的验证器。 例如,标题字段具有字符串 'Title'
作为标签,以及两个验证器:
InputRequired
:表示该字段不应为空。Length
:接受两个参数;min
设置为10
以确保标题至少有 10 个字符长,并且max
设置为100
以确保它不会超过 100 个字符。
描述文本区域字段有一个 InputRequired
验证器和一个 Length
验证器,其中 max
参数设置为 200
,[ 没有值X158X] 参数,表示唯一的要求是不超过 200 个字符。
同样,您为课程的价格定义了一个必填的整数字段,称为 price
。
level
字段是一个有多个选择的单选字段。 您在 Python 列表中定义选项并将其传递给 choices
参数。 您还可以使用 InputRequired
验证器根据需要定义字段。
available
字段是一个复选框字段。 您可以通过将默认值 'checked'
传递给 default
参数来设置它。 这意味着在添加新课程时将选中该复选框,除非用户取消选中它,这意味着课程默认可用。
有关如何使用 WTForms 库的更多信息,请参阅 WTForms 文档上的 Crash Course 页面。 有关更多字段,请参见 Fields 页面,有关验证表单数据的更多验证器,请参见 Validators 页面。
您已经在 forms.py
文件中配置了 Web 表单。 接下来,您将创建一个 Flask 应用程序,导入此表单,并在索引页面上显示其字段。 您还将在另一个页面上显示课程列表。
第 3 步 — 显示 Web 表单和课程
在这一步中,您将创建一个 Flask 应用程序,在索引页面上显示您在上一步中创建的 Web 表单,并创建一个课程列表和一个用于在其上显示课程的页面。
激活编程环境并安装 Flask 后,在 flask_app
目录中打开一个名为 app.py
的文件进行编辑:
nano app.py
该文件将从 Flask 中导入必要的类和助手,并从 forms.py
文件中导入 CourseForm
。 您将构建课程列表,然后实例化表单并将其传递给模板文件。 将以下代码添加到 app.py
:
烧瓶应用程序/app.py
from flask import Flask, render_template, redirect, url_for from forms import CourseForm app = Flask(__name__) app.config['SECRET_KEY'] = 'your secret key' courses_list = [{ 'title': 'Python 101', 'description': 'Learn Python basics', 'price': 34, 'available': True, 'level': 'Beginner' }] @app.route('/', methods=('GET', 'POST')) def index(): form = CourseForm() return render_template('index.html', form=form)
保存并关闭文件。
在这里,您从 Flask 导入以下内容:
Flask
类用于创建 Flask 应用程序实例。render_template()
函数用于渲染索引模板。- 添加新课程后,
redirect()
功能将用户重定向到课程页面。 - 用于构建 URL 的
url_for()
函数。
首先从 forms.py
文件中导入 CourseForm()
类,然后创建一个名为 app
的 Flask 应用程序实例。
您为 WTForms 设置了 密钥 配置,以便在生成 CSRF 令牌以保护您的 Web 表单时使用。 密钥应该是一个很长的随机字符串。 有关如何获取密钥的更多信息,请参阅 如何在 Flask 应用程序中使用 Web 表单 的第 3 步。
然后,您创建一个名为 courses_list
的词典列表,其中当前有一个词典,其中包含一个名为 'Python 101'
的示例课程。 在这里,出于演示目的,您使用 Python 列表作为数据存储。 在实际场景中,您将使用 SQLite 等数据库。 请参阅 如何在 Flask 应用程序中使用 SQLite 数据库 以了解如何使用数据库来存储课程数据。
您使用 index()
视图函数上的 app.route()
装饰器创建 /
主路由。 它在 methods
参数中同时接受 GET
和 POST
HTTP 方法。 GET 方法用于检索数据,POST 请求用于将数据发送到服务器,例如通过 Web 表单。 有关更多信息,请参阅 如何在 Flask 应用程序中使用 Web 表单。
您实例化表示 Web 表单的 CourseForm()
类,并将实例保存在名为 form
的变量中。 然后返回对 render_template()
函数的调用,将一个名为 index.html
的模板文件和表单实例传递给它。
要在索引页面上显示 Web 表单,您将首先创建一个基本模板,该模板将包含其他模板也将使用的所有基本 HTML 代码,以避免代码重复。 然后,您将创建在 index()
函数中呈现的 index.html
模板文件。 要了解有关模板的更多信息,请参阅 如何在 Flask 应用程序中使用模板。
在 Flask 搜索模板的 flask_app
目录中创建一个 templates
文件夹,然后打开一个名为 base.html
的模板文件,它将作为其他模板的基本模板:
mkdir templates nano templates/base.html
在 base.html
文件中添加以下代码以创建带有导航栏和内容块的基本模板:
flask_app/templates/base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %} - FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
此基本模板包含您需要在其他模板中重用的所有 HTML 样板。 title
块将被替换为每个页面设置标题,content
块将被替换为每个页面的内容。 导航栏有两个链接,一个用于索引页面,您使用 url_for()
辅助函数链接到 index()
视图函数,另一个用于“关于”页面(如果您选择包含一个)在您的应用程序中。
保存并关闭文件。
接下来,打开一个名为 index.html
的模板。 这是您在 app.py
文件中引用的模板:
nano templates/index.html
该文件将包含您通过 form
变量传递给 index.html
模板的 Web 表单。 向其中添加以下代码:
flask_app/templates/index.html
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Add a New Course {% endblock %}</h1> <form method="POST" action="/"> {{ form.csrf_token }} <p> {{ form.title.label }} {{ form.title(size=20) }} </p> {% if form.title.errors %} <ul class="errors"> {% for error in form.title.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.description.label }} </p> {{ form.description(rows=10, cols=50) }} {% if form.description.errors %} <ul class="errors"> {% for error in form.description.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.price.label }} {{ form.price() }} </p> {% if form.price.errors %} <ul class="errors"> {% for error in form.price.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.available() }} {{ form.available.label }} </p> {% if form.available.errors %} <ul class="errors"> {% for error in form.available.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> {{ form.level.label }} {{ form.level() }} </p> {% if form.level.errors %} <ul class="errors"> {% for error in form.level.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} <p> <input type="submit" value="Add"> </p> </form> {% endblock %}
保存并关闭文件。
您扩展基本模板,并在 <h1>
标记中设置标题。 然后在 <form>
标记内渲染 Web 表单字段,将其方法设置为 POST
并将操作设置为 /
主路由,即索引页面。 您首先使用 模板:Form.csrf token
行呈现 CSRF 令牌 WTForms 用于保护您的表单免受 CSRF 攻击。 该令牌与其余的表单数据一起发送到服务器。 请记住始终呈现此令牌以保护您的表单。
您使用语法 form.field()
呈现每个字段,并使用语法 form.field.label
呈现其标签。 您可以将参数传递给字段以控制其显示方式。 例如,您在模板:Form.title(size=20)
中设置标题输入字段的大小,通过参数rows
和cols
设置描述文本区域的行数和列数与通常在 HTML 中执行的方式相同。 您可以使用相同的方法将额外的 HTML 属性传递给字段,例如 class
属性以设置 CSS 类。
使用语法 if form.field.errors
检查验证错误。 如果某个字段有错误,则使用 for
循环遍历它们并将它们显示在该字段下方的列表中。
在激活虚拟环境的 flask_app
目录中,使用 FLASK_APP
环境变量告诉 Flask 应用程序(在本例中为 app.py
)。 然后将 FLASK_ENV
环境变量设置为 development
以在开发模式下运行应用程序并访问调试器。 有关 Flask 调试器的更多信息,请参阅 如何处理 Flask 应用程序中的错误 。 使用以下命令执行此操作(在 Windows 上,使用 set
而不是 export
):
export FLASK_APP=app export FLASK_ENV=development
接下来,运行应用程序:
flask run
在开发服务器运行的情况下,使用浏览器访问以下 URL:
http://127.0.0.1:5000/
您将看到索引页面上显示的 Web 表单:
尝试在不填写标题的情况下提交表单。 您将看到一条错误消息,通知您标题为必填项。 通过提交无效数据(例如长度少于 10 个字符的短标题或长度超过 200 个字符的描述)来试验表单以查看其他错误消息。
到目前为止,用有效数据填充表单什么也没做,因为您没有处理表单提交的代码。 稍后您将为此添加代码。
现在,您需要一个页面来显示列表中的课程。 稍后,处理 Web 表单数据会将新课程添加到列表中,并将用户重定向到课程页面以查看添加到其中的新课程。
让开发服务器保持运行并打开另一个终端窗口。
接下来,打开app.py
添加课程路线:
nano app.py
在文件末尾添加以下路由:
烧瓶应用程序/app.py
# ... @app.route('/courses/') def courses(): return render_template('courses.html', courses_list=courses_list)
保存并关闭文件。
该路由渲染一个名为 courses.html
的模板,并将 courses_list
列表传递给它。
然后创建 courses.html
模板来展示课程:
nano templates/courses.html
向其中添加以下代码:
flask_app/templates/courses.html
{% extends 'base.html' %} {% block content %} <h1>{% block title %} Courses {% endblock %}</h1> <hr> {% for course in courses_list %} <h2> {{ course['title'] }} </h2> <h4> {{ course['description'] }} </h4> <p> {{ course['price'] }}$ </p> <p><i>({{ course['level'] }})</i></p> <p>Availability: {% if course['available'] %} Available {% else %} Not Available {% endif %}</p> <hr> {% endfor %} {% endblock %}
保存并关闭文件。
您设置标题并循环浏览 courses_list
列表中的项目。 您在 <h2>
标签中显示标题,在 <h4>
标签中显示描述,在 <p>
标签中显示价格和课程级别。 使用条件 if course['available']
检查课程是否可用。 如果课程可用,则显示文本“可用”,如果不可用,则显示文本“不可用”。
使用浏览器转到课程页面:
http://127.0.0.1:5000/courses/
您将看到一个显示一门课程的页面,因为到目前为止您的课程列表中只有一门课程:
接下来,打开 base.html
在导航栏中添加指向课程页面的链接:
nano templates/base.html
将其编辑为如下所示:
flask_app/templates/base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %} {% endblock %} - FlaskApp</title> <style> nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } </style> </head> <body> <nav> <a href="{{ url_for('index') }}">FlaskApp</a> <a href="{{ url_for('courses') }}">Courses</a> <a href="#">About</a> </nav> <hr> <div class="content"> {% block content %} {% endblock %} </div> </body> </html>
保存并关闭文件。
刷新索引页面,您会在导航栏中看到一个新的 Courses 链接。
您已经创建了您的应用程序所需的页面:一个带有用于添加新课程的 Web 表单的索引页面和一个用于显示您在列表中的课程的页面。
为了使应用程序正常运行,您需要在用户提交 Web 表单数据时对其进行验证并将其添加到课程列表中来处理它。 接下来您将执行此操作。
第 4 步 - 访问表单数据
在此步骤中,您将访问用户提交的数据、对其进行验证并将其添加到课程列表中。
打开 app.py
在 index()
函数中添加处理 Web 表单数据的代码:
nano app.py
编辑 index()
函数,如下所示:
烧瓶应用程序/app.py
# ... @app.route('/', methods=('GET', 'POST')) def index(): form = CourseForm() if form.validate_on_submit(): courses_list.append({'title': form.title.data, 'description': form.description.data, 'price': form.price.data, 'available': form.available.data, 'level': form.level.data }) return redirect(url_for('courses')) return render_template('index.html', form=form)
保存并关闭文件。 在这里,您在 form
对象上调用 validate_on_submit()
方法,该方法检查请求是否为 POST 请求,并运行您为每个字段配置的验证器。 如果至少有一个验证器返回错误,则条件将为 False
,并且每个错误将显示在导致它的字段下方。
如果提交的表单数据有效,则条件为True
,执行if
语句下面的代码。 您构建一个课程词典,并使用 append
方法将新课程添加到 courses_list
列表中。 您可以使用语法 form.field.data
访问每个字段的值。 将新课程词典添加到课程列表后,将用户重定向到 Courses
页面。
在开发服务器运行的情况下,访问索引页面:
http://127.0.0.1:5000/
用有效数据填写表格并提交。 您将被重定向到 Courses
页面,您会看到新课程显示在上面。
结论
您创建了一个 Flask 应用程序,其中包含您使用 Flask-WTF 扩展和 WTForms 库构建的 Web 表单。 该表单有几种类型的字段来接收来自用户的数据,使用特殊的 WTForms 验证器对其进行验证,并将其添加到数据存储中。
如果您想了解更多关于 Flask 的信息,请查看 如何使用 Flask 系列创建网站中的其他教程。