如何使用 Flask-WTF 验证网络表单

作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。

介绍

网页表单,如文本字段和文本区域,让用户能够将数据发送到应用程序,无论是下载或无线电按钮,该应用程序将使用执行一个操作,或发送大量的文本区域进行处理或显示,例如,在社交媒体应用程序中,您可能给用户一个框,他们可以将新内容添加到他们的页面。

Flask是一个轻量级的Python网页框架,提供用于在Python语言中创建网页应用程序的有用的工具和功能。为了在Flask中以安全和灵活的方式渲染和验证网页表单,您将使用Flask-WTF(https://flask-wtf.readthedocs.io/en/1.0.x/),这是一个Flask扩展,可帮助您在Flask应用程序中使用WTForms库。

WTForms是一个 Python 库,提供灵活的 Web 表单渲染。 您可以使用它来渲染文本字段、文本区域、密码字段、无线电按钮等。 WTForms 还提供使用不同的 validators 进行强大的数据验证,以验证用户提交的数据符合您定义的某些标准。 例如,如果您有所需的字段,您可以确保用户提交的数据提供,或具有一定的长度。

WTForms 还使用 CSRF 代币来提供保护,以防止 CSRF 攻击,这些攻击允许攻击者在用户身份验证的 Web 应用程序上执行不必要的操作。成功的 CSRF 攻击可以迫使用户执行状态改变的请求,例如将资金转移到攻击者的银行账户中,在银行应用程序中更改用户的电子邮件地址等。

在本教程中,您将构建一个小型的Web应用程序,该应用程序将有一个页面,用于显示存储在Python列表中的课程,而索引页面将有一个形式,用于输入课程标题,其描述,价格,可用性和水平(初学者,中级或高级)。

前提条件

*本地 Python 3 编程环境. 在 [如何安装和设置Python 3的本地编程环境]系列(https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3)中跟随操作系统的教程. 在本教程中,您将调用项目目录 flask_app

步骤 1 — 安装 Flask 和 Flask-WTF

在此步骤中,您将安装 Flask 和 Flask-WTF,这也将自动安装 WTForms 库。

随着虚拟环境的启用,使用管道来安装Flask和Flask-WTF:

1pip install Flask Flask-WTF

一旦安装成功完成,您将在输出结束时看到一个类似于以下的行:

1[secondary_label Output]
2Successfully 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包的依赖性。

现在你已经安装了所需的Python包,你将下一步设置一个Web表单。

步骤2 - 设置表格

在此步骤中,您将使用从 WTForms 库导入的字段和验证器设置 Web 表单。

您将设置以下领域:

  • **标题:**课程标题的文本输入字段
  • **描述:**课程描述的文本区域字段
  • **价格:**课程价格的整数字段
  • **水平:**课程级别的无线电字段有三种选择:初学者,中学者和高级
  • **可用:**一个检查框字段,表示课程是否目前可用

首先,在您的flask_app目录中打开一个名为forms.py的新文件,该文件将包含您在应用程序中需要的表单:

1nano forms.py

此文件将有一个代表您的 Web 表格的类别. 添加以下导入在顶部:

1[label flask_app/forms.py]
2from flask_wtf import FlaskForm
3from wtforms import (StringField, TextAreaField, IntegerField, BooleanField,
4                     RadioField)
5from wtforms.validators import InputRequired, Length

要构建网页表格,您将创建一个FlaskForm基础类的子类,您将其从flask_wtf包中导入。

您从 WTForms 库中导入以下 字段:

从 wtforms.validators 导入InputRequired,长度行中,您将导入验证器用于字段,以确保用户提交有效数据。

接下来,在进口语句后添加下面的 :

 1[label flask_app/forms.py]
 2
 3class CourseForm(FlaskForm):
 4    title = StringField('Title', validators=[InputRequired(),
 5                                             Length(min=10, max=100)])
 6    description = TextAreaField('Course Description',
 7                                validators=[InputRequired(),
 8                                            Length(max=200)])
 9    price = IntegerField('Price', validators=[InputRequired()])
10    level = RadioField('Level',
11                       choices=['Beginner', 'Intermediate', 'Advanced'],
12                       validators=[InputRequired()])
13    available = BooleanField('Available', default='checked')

保存并关闭文件。

在此CourseForm类中,您将继承从您之前导入的FlaskForm基础类。 您将表单字段的集合定义为类变量,使用您从 WTForms 库导入的表单字段。

您可以通过从wtforms.validators模块中导入的验证器列表来定义每个字段的验证器。

  • InputRequired: 表示该字段不应该是空的
  • 长度: 需要两个参数; min 设置为 10' 以确保标题长度至少为 10 个字符,而 max设置为100' 以确保标题长度不超过 100 个字符。

描述文本区域域有一个InputRequired验证器和一个Length验证器,其max参数设置为200,没有min参数的值,这意味着唯一的要求是它不超过200个字符。

类似地,您定义了名为价格的课程价格所需的整数字段。

水平字段是一个具有多个选择的无线电字段,您在Python列表中定义选项并将其传输到选择参数中,您还使用InputRequired验证器根据需要定义该字段。

可用字段是一个检查框的字段. 您通过将其转移到默认参数来设置默认的已检查值。

有关如何使用 WTForms 库的更多信息,请参阅 WTForms 文档上的 Crash Course 页面

您已经在forms.py文件中配置了网页表格,接下来,您将创建一个Flask应用程序,导入该表格,并在索引页面上显示其字段。

步骤 3 – 显示 Web 表格和课程

在此步骤中,您将创建一个Flask应用程序,在索引页面上显示您在上一步创建的Web表单,并创建课程列表和页面以显示课程。

当您的编程环境启用并安装Flask时,请打开名为app.py的文件,在您的flask_app目录中进行编辑:

1nano app.py

此文件将从 Flask 导入所需的类和助手,并从 forms.py 文件中导入 CourseForm. 您将构建课程列表,然后实例表格并将其传输到模板文件中。

 1[label flask_app/app.py]
 2from flask import Flask, render_template, redirect, url_for
 3from forms import CourseForm
 4
 5app = Flask(__name__)
 6app.config['SECRET_KEY'] = 'your secret key'
 7
 8courses_list = [{
 9    'title': 'Python 101',
10    'description': 'Learn Python basics',
11    'price': 34,
12    'available': True,
13    'level': 'Beginner'
14    }]
15
16@app.route('/', methods=('GET', 'POST'))
17def index():
18    form = CourseForm()
19    return render_template('index.html', form=form)

保存并关闭文件。

以下是从 Flask 导入的内容:

  • 创建 Flask 应用程序实例的 Flask
  • 返回索引模板的 render_template() 函数
  • 重定向() 函数 在添加新课程后将用户重定向到课程页面
  • 构建 URL 的 url_for() 函数

首先从forms.py文件中导入CourseForm()类,然后创建一个名为app的Flask应用程序实例。

您已为 WTForms 设置一个 secret key 配置,用于生成 CSRF 代币来保护您的 Web 表单. 秘密密密钥应该是一个长的随机字符串. 请参阅 如何在 Flask 应用程序中使用 Web 表单的步骤 3 有关如何获取秘密密密钥的更多信息。

然后你创建一个名为courses_list的字典列表,该词典目前有一个名为Python 101的示例课程。在这里,你将使用Python列表作为演示目的的数据存储库。在现实世界的情况下,你将使用SQLite等数据库。 参阅如何在Flask应用程序中使用SQLite数据库(https://andsky.com/tech/tutorials/how-to-use-an-sqlite-database-in-a-flask-application),了解如何使用数据库来存储你的课程数据。

您使用app.route()装饰器在index()视图函数中创建一个/主路线,它在methods参数中接受GETPOST HTTP 方法

您实例化代表 Web 表单的 CourseForm() 类,并将该实例保存到名为 form 的变量中,然后返回对 render_template() 函数的呼叫,并将其传递给名为 index.html 的模板文件和表单实例。

要在索引页面上显示网页表格,您首先将创建一个基础模板,该模板将包含其他模板也将使用的所有基本HTML代码,以避免代码重复。

在您的flask_app目录中创建一个模板文件夹,Flask 搜索模板,然后打开名为base.html的模板文件,它将是其他模板的基板模板:

1mkdir templates
2nano templates/base.html

将下列代码添加到 base.html 文件中,以创建一个 navbar 和一个内容块的 base 模板:

 1[label flask_app/templates/base.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4<head>
 5    <meta charset="UTF-8">
 6    <title>{% block title %} {% endblock %} - FlaskApp</title>
 7    <style>
 8        nav a {
 9            color: #d64161;
10            font-size: 3em;
11            margin-left: 50px;
12            text-decoration: none;
13        }
14    </style>
15</head>
16<body>
17    <nav>
18        <a href="{{ url_for('index') }}">FlaskApp</a>
19        <a href="#">About</a>
20    </nav>
21    <hr>
22    <div class="content">
23        {% block content %} {% endblock %}
24    </div>
25</body>
26</html>

此基础模板包含您在其他模板中需要重复使用的所有HTML锅炉板。标题块将被更换为为每个页面设置一个标题,而内容块将被替换为每个页面的内容。导航栏有两个链接,一个用于索引页面,其中您使用url_for()辅助函数链接到index()视图函数,另一个用于关于页面,如果您选择在应用程序中包含一个链接。

保存并关闭文件。

接下来,打开一个名为index.html的模板,这是你在app.py文件中提到的模板:

1nano templates/index.html

此文件将包含您通过form变量传递到index.html模板的网页表格,添加以下代码:

 1[label flask_app/templates/index.html]
 2{% extends 'base.html' %}
 3
 4{% block content %}
 5    <h1>{% block title %} Add a New Course {% endblock %}</h1>
 6
 7    <form method="POST" action="/">
 8        {{ form.csrf_token }}
 9        <p>
10            {{ form.title.label }}
11            {{ form.title(size=20) }}
12        </p>
13
14        {% if form.title.errors %}
15            <ul class="errors">
16                {% for error in form.title.errors %}
17                    <li>{{ error }}</li>
18                {% endfor %}
19            </ul>
20        {% endif %}
21
22        <p>
23            {{ form.description.label }}
24        </p>
25        {{ form.description(rows=10, cols=50) }}
26
27        {% if form.description.errors %}
28            <ul class="errors">
29                {% for error in form.description.errors %}
30                    <li>{{ error }}</li>
31                {% endfor %}
32            </ul>
33        {% endif %}
34
35        <p>
36            {{ form.price.label }}
37            {{ form.price() }}
38        </p>
39
40        {% if form.price.errors %}
41            <ul class="errors">
42                {% for error in form.price.errors %}
43                    <li>{{ error }}</li>
44                {% endfor %}
45            </ul>
46        {% endif %}
47
48        <p>
49            {{ form.available() }} {{ form.available.label }}
50        </p>
51
52        {% if form.available.errors %}
53            <ul class="errors">
54                {% for error in form.available.errors %}
55                    <li>{{ error }}</li>
56                {% endfor %}
57            </ul>
58        {% endif %}
59
60        <p>
61            {{ form.level.label }}
62            {{ form.level() }}
63        </p>
64
65        {% if form.level.errors %}
66            <ul class="errors">
67                {% for error in form.level.errors %}
68                    <li>{{ error }}</li>
69                {% endfor %}
70            </ul>
71        {% endif %}
72
73        <p>
74            <input type="submit" value="Add">
75        </p>
76    </form>
77
78{% endblock %}

保存并关闭文件。

您将基础模板扩展,并在一个<h1>标签中设置一个标题,然后您将网页表单字段转换为<form>标签,将其方法设置为POST,并将操作转移到/主路线,即索引页面。

您可以使用form.field.label语法渲染每个字段,并使用form.field.label语法渲染其标签。您可以将参数传输到该字段以控制其显示方式。例如,您将标题输入字段的大小设置为{{form.title(size=20) }},并通过参数cols来设置描述文本区域的行和列的数量,就像您通常在HTML中所做的那样。

您可以使用语法if form.field.errors检查验证错误,如果一个字段有错误,则使用for循环循环循环通过它们,并在字段下面列出它们。

在您的flask_app目录中启用虚拟环境时,请使用FLASK_APP环境变量告诉Flask关于该应用程序(app.py在这种情况下)。然后将FLASK_ENV环境变量设置为development,以便在开发模式下运行应用程序,并获取调试器的访问。有关Flask调试器的更多信息,请参阅如何处理Flask应用程序中的错误(LINK0))。

1export FLASK_APP=app
2export FLASK_ENV=development

接下来,运行应用程序:

1flask run

当开发服务器运行时,请使用您的浏览器访问以下URL:

1http://127.0.0.1:5000/

您将看到在索引页面上显示的 Web 表格:

Index Page

尝试提交表单而不填写标题. 你会看到一个错误消息,通知你标题是必需的. 通过提交错误的数据(如短标题小于10个字符长度,或描述超过200个字符长度)来查看其他错误消息来尝试表单。

用有效的数据填写表单到目前为止没有任何效果,因为您没有处理表单提交的代码。

现在,您需要一个页面来显示您在列表中的课程. 后来,处理网页表格数据将添加一个新课程到列表,并将用户重定向到课程页面,以查看新课程添加到它。

让开发服务器运行,然后打开另一个终端窗口。

接下来,打开app.py来添加课程路线:

1nano app.py

添加下列路径到文件的末尾:

1[label flask_app/app.py]
2# ...
3
4@app.route('/courses/')
5def courses():
6    return render_template('courses.html', courses_list=courses_list)

保存并关闭文件。

此路径将显示一个名为courses.html的模板,并将其传输到courses_list列表中。

然后创建courses.html模板以显示课程:

1nano templates/courses.html

添加以下代码:

 1[label flask_app/templates/courses.html]
 2{% extends 'base.html' %}
 3
 4{% block content %}
 5    <h1>{% block title %} Courses {% endblock %}</h1>
 6    <hr>
 7    {% for course in courses_list %}
 8        <h2> {{ course['title'] }} </h2>
 9        <h4> {{ course['description'] }} </h4>
10        <p> {{ course['price'] }}$ </p>
11        <p><i>({{ course['level'] }})</i></p>
12        <p>Availability:
13            {% if course['available'] %}
14                Available
15            {% else %}
16                Not Available
17            {% endif %}</p>
18        <hr>
19    {% endfor %}
20{% endblock %}

保存并关闭文件。

您通过courses_list列表中的项目设置一个标题和循环。您将标题显示在<h2>标签中,描述显示在<h4>标签中,价格和课程水平显示在<p>标签中。

使用您的浏览器前往课程页面:

1http://127.0.0.1:5000/courses/

你会看到一页显示一个课程,因为到目前为止,你的课程列表中只有一门课程:

Courses Page

接下来,打开base.html,在导航栏中添加链接到课程页面:

1nano templates/base.html

编辑它看起来如下:

 1[label flask_app/templates/base.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4<head>
 5    <meta charset="UTF-8">
 6    <title>{% block title %} {% endblock %} - FlaskApp</title>
 7    <style>
 8        nav a {
 9            color: #d64161;
10            font-size: 3em;
11            margin-left: 50px;
12            text-decoration: none;
13        }
14    </style>
15</head>
16<body>
17    <nav>
18        <a href="{{ url_for('index') }}">FlaskApp</a>
19        <a href="{{ url_for('courses') }}">Courses</a>
20        <a href="#">About</a>
21    </nav>
22    <hr>
23    <div class="content">
24        {% block content %} {% endblock %}
25    </div>
26</body>
27</html>

保存并关闭文件。

更新索引页面,您将在导航栏中看到一个新的 课程链接。

您已经创建了应用程序所需的页面:一个索引页面,包含添加新课程的网页表,以及显示您列出的课程的页面。

要使应用程序功能,您需要通过验证它并将其添加到课程列表来处理用户提交的Web表单数据。

步骤 4 – 访问表单数据

在此步骤中,您将访问用户提交的数据,验证并将其添加到课程列表中。

打开app.py以添加用于在index()函数内处理 Web 表单数据的代码:

1nano app.py

编辑index()函数以显示如下:

 1[label flask_app/app.py]
 2# ...
 3@app.route('/', methods=('GET', 'POST'))
 4def index():
 5    form = CourseForm()
 6    if form.validate_on_submit():
 7        courses_list.append({'title': form.title.data,
 8                             'description': form.description.data,
 9                             'price': form.price.data,
10                             'available': form.available.data,
11                             'level': form.level.data
12                             })
13        return redirect(url_for('courses'))
14    return render_template('index.html', form=form)

保存和关闭文件 在这里,您在表单对象上呼叫validate_on_submit()方法,该方法会检查请求是否是一个 POST 请求,并运行您为每个字段配置的验证器。

如果提交的表单数据是有效的,则条件为,并且将执行如果声明下的代码。您构建一个课程字典,并使用附加方法将新课程添加到courses_list列表中。

随着开发服务器运行,请访问索引页面:

1http://127.0.0.1:5000/

用有效的数据填写表格并提交它,您将被重定向到课程页面,您将看到新课程显示在其上。

结论

您创建了一个 Flask 应用程序,该应用程序具有使用 Flask-WTF 扩展和 WTForms 库构建的 Web 表单,该表单有几种类型的字段来接收用户的数据,使用特殊的 WTForms 验证器验证它,并将其添加到数据存储中。

如果您想了解更多关于 Flask 的信息,请参阅 如何使用 Flask 创建网站系列的其他教程。

Published At
Categories with 技术
comments powered by Disqus