如何在Flask应用程序中使用网络表单

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

引言

网页表单,如文本字段和文本区域,允许用户向您的应用程序发送数据,以便执行操作,或提交较大篇幅的文本。例如,在社交媒体应用中,您可能会为用户提供一个框,让他们可以添加新内容到他们的页面。另一个例子是登录页面,您会为用户提供一个文本字段来输入用户名,以及一个密码字段来输入密码。服务器(本例中为您的Flask应用)会使用用户提交的数据,如果数据有效则允许用户登录,否则会返回如无效凭证!的信息,告知用户提交的数据不正确。

Flask 是一个轻量级的 Python Web 框架,为使用 Python 语言创建 Web 应用程序提供了有用的工具和功能。在本教程中,您将构建一个小型 Web 应用程序,演示如何使用 Web 表单。该应用程序将有一个页面用于显示存储在 Python 列表中的消息,以及一个页面用于添加新消息。您还将使用 消息闪现 来通知用户在提交无效数据时发生的错误。

前提条件

第一步 —— 显示消息

在这一步中,你将创建一个Flask应用程序,其首页用于显示存储在Python字典列表中的消息。

首先,打开一个名为app.py的新文件进行编辑:

  1. nano app.py

app.py文件中添加以下代码,创建一个带有单一路由的Flask服务器:

flask_app/app.py
from flask import Flask, render_template

app = Flask(__name__)

messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]

@app.route('/')
def index():
    return render_template('index.html', messages=messages)

保存并关闭文件。

在此文件中,你首先从flask包中导入了Flask类和render_template()函数。然后,使用Flask类创建了一个名为app应用实例,传入了特殊的__name__变量,Flask需要这个变量来在幕后设置一些路径。模板渲染的内容在教程如何在Flask应用中使用模板中有详细介绍。

接着,你创建了一个名为messages的全局Python列表,其中包含字典。每个字典包含两个键:title用于消息标题,content用于消息内容。这是一个简化的数据存储示例;在实际应用中,你会使用一个持久化存储数据的数据库,并能更高效地操作这些数据。

在创建了Python列表后,您使用@app.route()装饰器来创建一个名为index()视图函数。在其中,您返回对render_template()函数的调用,这指示Flask该路由应显示一个HTML模板。您将此模板命名为index.html(稍后将创建它),并向其传递一个名为messages的变量。该变量持有您之前声明的messages列表作为其值,并使其对HTML模板可用。视图函数在教程如何使用Flask和Python 3创建您的第一个Web应用程序中有详细介绍。

接下来,在您的flask_app目录中创建一个templates文件夹,Flask会在其中搜索模板,然后打开一个名为base.html的模板文件,该文件将包含其他模板继承的代码以避免代码重复:

  1. mkdir templates
  2. 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>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        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`文件中引用的模板:

  1. nano templates/index.html

向其中添加以下代码:

flask_app/templates/index.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Messages {% endblock %}</h1>
    {% for message in messages %}
        <div class='message'>
            <h3>{{ message['title'] }}</h3>
            <p>{{ message['content'] }}</p>
        </div>
    {% endfor %}
{% endblock %}

保存并关闭文件。

在这段代码中,你扩展了`base.html`模板并替换了`content`块的内容。你使用了一个`

`标题,同时也作为页面的标题。

你在`{% for message in messages %}`这一行中使用了Jinja的`for`循环来遍历`messages`列表中的每条消息。你使用了一个`

`标签来包含消息的标题和内容。标题显示在一个`

`标题中,内容显示在一个`

`标签中。

在你的虚拟环境激活的情况下,在`flask_app`目录中,使用`FLASK_APP`环境变量告诉Flask关于应用的信息(在本例中是`app.py`):

  1. export FLASK_APP=app

接着,将FLASK_ENV环境变量设置为development,以便在开发模式下运行应用程序并启用调试器。有关Flask调试器的更多信息,请参阅如何在Flask应用程序中处理错误。使用以下命令进行设置(在Windows上,使用set代替export):

  1. export FLASK_ENV=development

接下来,运行应用程序:

  1. flask run

开发服务器启动后,通过浏览器访问以下URL:

http://127.0.0.1:5000/

你将在索引页上看到messages列表中的消息:

现在你已经搭建了Web应用程序并展示了消息,接下来需要一种方式让用户能够向索引页添加新消息。这可以通过网页表单来实现,你将在下一步中进行设置。

步骤2 — 设置表单

在这一步中,你将在应用程序中创建一个页面,允许用户通过网页表单向消息列表添加新消息。

保持开发服务器运行,并打开一个新的终端窗口。

首先,打开你的app.py文件:

  1. nano app.py

在文件末尾添加以下路由:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

保存并关闭文件。

这个 `/create` 路由具有 `methods` 参数,其值为元组 `('GET', 'POST')`,以接受 `GET` 和 `POST` 请求。`GET` 和 `POST` 是 `HTTP 方法`。默认情况下,仅接受 `GET` 请求,这些请求用于检索数据,例如向服务器请求索引页或关于页。`POST` 请求用于向特定路由提交数据,这通常会改变服务器上的数据。

在此示例中,您将使用 `GET` 请求请求 `create` 页面。Create 页面将包含一个带有输入字段和提交按钮的网页表单。当用户填写网页表单并点击提交按钮时,会向 `/create` 路由发送 `POST` 请求。在那里处理请求,验证提交的数据以确保用户未提交空表单,并将其添加到 `messages` 列表中。

`create()` 视图函数目前只做一件事:在接收到常规 GET 请求时渲染名为 `create.html` 的模板。您现在将创建此模板,然后在下一步中编辑该函数以处理 `POST` 请求。

打开一个名为 `create.html` 的新模板文件:

  1. nano templates/create.html

向其中添加以下代码:

flask_app/templates/create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Message {% endblock %}</h1>
    <form method="post">
        <label for="title">Title</label>
        <br>
        <input type="text" name="title"
               placeholder="Message title"
               value="{{ request.form['title'] }}"></input>
        <br>

        <label for="content">Message Content</label>
        <br>
        <textarea name="content"
                  placeholder="Message content"
                  rows="15"
                  cols="60"
                  >{{ request.form['content'] }}</textarea>
        <br>
        <button type="submit">Submit</button>
    </form>
{% endblock %}

保存并关闭文件。

在这段代码中,你扩展了 `base.html` 模板,并用一个 `<h1>` 标题替换了 `content` 块,该标题作为页面的标题。在 `<form>` 标签中,你将 `method` 属性设置为 `post`,以便表单数据作为 `POST` 请求发送到服务器。

在表单中,你有一个名为 `title` 的文本输入字段;这是你在应用程序中用来访问标题表单数据的名字。你为 `<input>` 标签设置了一个 `value` 为 `{{ request.form['title'] }}`。这很有用,可以恢复用户输入的数据,以防万一出现问题时数据不会丢失。例如,如果用户忘记了填写必需的 `content` 文本区域,请求会被发送到服务器,并返回一个错误消息作为响应,但标题中的数据不会丢失,因为它会被保存在 `request` 全局对象中,可以通过 `request.form['title']` 访问。

在标题输入字段之后,你添加了一个名为 `content` 的文本区域,其值为 `{{ request.form['content'] }}`,原因如前所述。

最后,你在表单末尾添加了一个提交按钮。

现在,在开发服务器运行的情况下,使用浏览器导航到 `/create` 路由:

http://127.0.0.1:5000/create

你会看到一个“添加新消息”页面,其中有一个用于消息标题的输入字段,一个用于消息内容的文本区域,以及一个提交按钮。

此表单向你的 `create()` 视图函数提交一个 `POST` 请求。然而,该函数中尚无处理 `POST` 请求的代码,因此在填写表单并提交后没有任何反应。下一步,你将处理表单提交时传入的 `POST` 请求。你将检查提交的数据是否有效(非空),并将消息标题和内容添加到 `messages` 列表中。

步骤 3 — 处理表单请求

在这一步中,你将在应用端处理表单请求。你将通过上一步创建的表单访问用户提交的数据,并将其添加到消息列表中。你还将使用消息闪现功能,在用户提交无效数据时通知他们。闪现消息仅显示一次,并在下一个请求时消失(例如,如果你导航到另一个页面)。

打开 app.py 文件进行编辑:

  1. nano app.py

首先,从 Flask 框架导入以下内容:

  • 全局request对象,用于访问上一步构建的 HTML 表单提交的传入请求数据。
  • url_for() 函数用于生成URL。
  • flash() 函数用于在请求处理后闪现一条消息(告知用户一切顺利,或在提交的数据无效时通知他们存在问题)。
  • redirect() 函数用于将客户端重定向到其他位置。

将这些导入添加到文件的第一行:

flask_app/app.py
from flask import Flask, render_template, request, url_for, flash, redirect

# ...

flash() 函数将闪现消息存储在客户端的浏览器会话中,这需要设置一个密钥。此密钥用于保护会话,使Flask能够记住从一个请求到另一个请求的信息,例如从新消息页面转到索引页面。用户可以访问存储在会话中的信息,但不能修改,除非他们拥有密钥,因此绝不能让任何人访问你的密钥。更多信息请参阅Flask会话文档

密钥应是一个长随机字符串。你可以使用os模块中的os.urandom()方法生成密钥,该方法返回适用于加密用途的随机字节字符串。要使用它获取随机字符串,请打开一个新的终端并使用以下命令启动Python交互式 shell:

  1. python

在Python交互式 shell 中,从标准库导入os模块,并按如下方式调用os.urandom()方法:

  1. import os
  2. os.urandom(24).hex()

你将得到类似以下内容的字符串:

Output
'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'

你可以将获得的字符串用作你的密钥。

要设置密钥,通过app.config对象为你的应用添加一个SECRET_KEY配置。在定义messages变量之前,紧跟在app定义之后直接添加它:

flask_app/app.py

# ...
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'


messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]
# ...

接下来,修改create()视图函数,使其完全如下所示:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        elif not content:
            flash('Content is required!')
        else:
            messages.append({'title': title, 'content': content})
            return redirect(url_for('index'))

    return render_template('create.html')

if语句中,通过比较request.method == 'POST'确保仅当请求为POST请求时执行其后的代码。

接着,从request.form对象中提取提交的标题和内容,该对象使您能够访问请求中的表单数据。如果未提供标题,条件if not title将被满足。在这种情况下,您将使用flash()函数向用户显示一条消息,告知他们标题是必需的。这会将消息添加到闪现消息列表中。稍后,您将在base.html模板中显示这些消息。同样,如果未提供内容,条件elif not content将被满足。如果是这样,您将'内容是必需的!'消息添加到闪现消息列表中。

如果消息的标题和内容已正确提交,您将使用messages.append({'title': title, 'content': content})语句将一个新字典添加到messages列表中,其中包含用户提供的标题和内容。然后,使用redirect()函数将用户重定向到索引页面。您使用url_for()函数链接到索引页面。

保存并关闭文件。

现在,使用您的网络浏览器导航到/create路由:

http://127.0.0.1:5000/create

在表单中填写您选择的标题和一些内容。提交表单后,您将在索引页上看到新消息的列表。

最后,您将在base.html模板中显示闪现消息,并在导航栏中添加“新消息”页面的链接,以便轻松访问此新页面。打开基础模板文件:

  1. nano templates/base.html

编辑文件,在导航栏内的FlaskApp链接后添加一个新的 `<a>` 标签。然后在 `<nav>` 标签内添加一个新的 `for` 循环,直接在 `content` 块上方显示闪现消息。这些消息可通过Flask提供的特殊函数 `get_flashed_messages()` 获取。接着为每条消息添加一个名为 `alert` 的类属性,并在 `<style>` 标签内赋予其一些CSS属性:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .alert {
            padding: 20px;
            margin: 5px;
            color: #970020;
            background-color: #ffd5de;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% for message in get_flashed_messages() %}
            <div class="alert">{{ message }}</div>
        {% endfor %}
        {% block content %} {% endblock %}
    </div>
</body>
</html>

保存并关闭文件,然后在浏览器中重新加载 `https://127.0.0.1:5000`。导航栏现在将有一个链接到 `/create` 路由的“创建”项。

要查看闪现消息的工作原理,请转到“创建”页面,并点击提交按钮而不填写两个字段。您将收到如下所示的消息:

返回索引页面,您会看到导航栏下方的闪现消息消失了,尽管它们作为基础模板的一部分显示出来。如果不是闪现消息,它们也会在索引页面上显示,因为索引页面也继承自基础模板。

尝试提交一个带有标题但没有内容的表单,你会看到消息“内容是必需的!”。点击导航栏中的FlaskApp链接返回首页,然后点击浏览器的返回按钮回到创建页面。你会发现提示消息仍然存在。这仅在点击返回按钮时有效,因为它保存了之前的请求。如果在导航栏中点击创建链接,则会发送一个新的请求,这将清空表单,因此闪现的消息也会消失。

现在你知道了如何接收用户输入、如何验证它,以及如何将其添加到数据源中。

注意:
你添加到messages列表中的消息在服务器停止时会消失,因为Python列表仅保存在内存中。若要永久保存你的消息,你需要使用SQLite等数据库。查看如何在Python 3中使用sqlite3模块以学习如何使用SQLite与Python。

结论

你创建了一个Flask应用,用户可以在首页显示的消息列表中添加消息。你创建了一个网页表单,处理用户通过表单提交的数据,并将其添加到你的消息列表中。你还使用了闪现消息来通知用户在提交无效数据时的错误。

若想深入了解 Flask,不妨查阅Flask系列中的其他教程

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-web-forms-in-a-flask-application