作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。
介绍
Web 表单(例如文本字段和文本区域)使用户能够将数据发送到您的应用程序以使用它来执行操作,或者将更大的文本区域发送到应用程序。 例如,在社交媒体应用程序中,您可能会为用户提供一个框,他们可以在其中向页面添加新内容。 另一个示例是登录页面,您可以在其中为用户提供一个文本字段以输入其用户名,并为用户提供一个密码字段以输入其密码。 服务器(在这种情况下是您的 Flask 应用程序)使用用户提交的数据,如果数据有效,则将其登录,或者使用 Invalid credentials!
之类的消息进行响应,以通知用户他们提交的数据不是正确的。
Flask 是一个轻量级的 Python Web 框架,它为使用 Python 语言创建 Web 应用程序提供了有用的工具和功能。 在本教程中,您将构建一个演示如何使用 Web 表单的小型 Web 应用程序。 该应用程序将有一个用于显示存储在 Python 列表中的消息的页面,以及一个用于添加新消息的页面。 当用户提交无效数据时,您还将使用 消息闪烁 通知用户错误。
先决条件
- 本地 Python 3 编程环境,请按照 如何为 Python 3 系列安装和设置本地编程环境中的分发教程进行操作。 在本教程中,我们将调用项目目录
flask_app
。 - 了解基本的 Flask 概念,例如路由、视图函数和模板。 如果您不熟悉 Flask,请查看 如何使用 Flask 和 Python 创建您的第一个 Web 应用程序 和 如何在 Flask 应用程序中使用模板。
- 了解基本的 HTML 概念。 您可以查看我们的 如何使用 HTML 构建网站系列教程了解背景知识。
第 1 步 — 显示消息
在这一步中,您将创建一个带有索引页面的 Flask 应用程序,用于显示存储在 Python 字典列表中的消息。
首先打开一个名为 app.py
的新文件进行编辑:
nano app.py
在 app.py
文件中添加以下代码以创建具有单个路由的 Flask 服务器:
烧瓶应用程序/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
的新 应用程序实例 ,传递 Flask 设置所需的特殊 __name__
变量幕后的一些路径。 教程 如何在 Flask 应用程序中使用模板 介绍了渲染模板。
然后创建一个名为 messages
的全局 Python 列表,其中包含 Python 字典。 每个字典有两个键:title
为消息标题,content
为消息内容。 这是数据存储方法的简化示例; 在实际场景中,您将使用永久保存数据并允许您更有效地操作它的数据库。
创建 Python 列表后,使用 @app.route()
装饰器创建一个名为 index()
的 视图函数 。 在其中,您返回对 render_template()
函数的调用,该函数向 Flask 指示路由应该显示 HTML 模板。 您将此模板命名为 index.html
(稍后将创建它),并将一个名为 messages
的变量传递给它。 此变量保存您之前声明为值的 messages
列表,并使其可用于 HTML 模板。 视图函数在教程 How To Create Your First Web Application Using Flask and Python 3 中有介绍。
接下来,在 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> .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
文件中引用的模板:
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
块的内容。 您使用的 <h1>
标题也用作标题。
在 {% for message in messages %}
行中使用 Jinja for loop 遍历 messages
列表中的每条消息。 您使用 <div>
标记来包含消息的标题和内容。 您在 <h3>
标题中显示标题,在 <p>
标记中显示内容。
在激活虚拟环境的 flask_app
目录中,使用 FLASK_APP
环境变量告诉 Flask 应用程序(在本例中为 app.py
):
export FLASK_APP=app
然后将 FLASK_ENV
环境变量设置为 development
以在开发模式下运行应用程序并访问调试器。 有关 Flask 调试器的更多信息,请参阅 如何处理 Flask 应用程序中的错误 。 使用以下命令执行此操作(在 Windows 上,使用 set
而不是 export
):
export FLASK_ENV=development
接下来,运行应用程序:
flask run
在开发服务器运行的情况下,使用浏览器访问以下 URL:
http://127.0.0.1:5000/
您将在索引页面上显示的 messages
列表中看到消息:
现在您已经设置了 Web 应用程序并显示了消息,您需要一种方法来允许用户将新消息添加到索引页面。 这是通过网络表单完成的,您将在下一步中进行设置。
第 2 步 — 设置表格
在此步骤中,您将在应用程序中创建一个页面,允许用户通过 Web 表单将新消息添加到消息列表中。
让开发服务器保持运行并打开一个新的终端窗口。
首先,打开您的 app.py
文件:
nano app.py
将以下路由添加到文件末尾:
烧瓶应用程序/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
页面。 创建页面将有一个带有输入字段和提交按钮的 Web 表单。 当用户填写 Web 表单并单击提交按钮时,会向 /create
路由发送 POST
请求。 在那里您处理请求,验证提交的数据以确保用户没有提交空表单,并将其添加到 messages
列表。
create()
视图函数目前只做一件事:当它收到一个常规的 GET 请求时,渲染一个名为 create.html
的模板。 您现在将创建此模板,然后在下一步中编辑函数以处理 POST
请求。
打开一个名为 create.html
的新模板文件:
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
模板并将 content
块替换为用作页面标题的 <h1>
标题。 在 <form>
标记中,您将 method
属性设置为 post
,以便表单数据作为 POST
请求发送到服务器。
在表单中,您有一个名为 title
的文本输入字段; 这是您将在应用程序上用于访问标题表单数据的名称。 你给 <input>
标签一个 {{ request.form['title'] }}
的 value
。 这对于恢复用户输入的数据很有用,因此在出现问题时不会丢失。 例如,如果用户忘记填写所需的 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
文件进行编辑:
nano app.py
首先,您将从 Flask 框架中导入以下内容:
- 全局 request 对象用于访问将通过您在上一步中构建的 HTML 表单提交的传入请求数据。
- url_for() 函数用于生成 URL。
- flash() 函数在处理请求时闪烁消息(通知用户一切顺利,或者如果提交的数据无效,则通知他们问题)。
- redirect() 函数将客户端重定向到不同的位置。
将这些导入添加到文件的第一行:
烧瓶应用程序/app.py
from flask import Flask, render_template, request, url_for, flash, redirect # ...
flash()
函数将闪过的消息存储在客户端的浏览器会话中,这需要设置一个密钥。 此密钥用于保护会话,这允许 Flask 记住从一个请求到另一个请求的信息,例如从新消息页面移动到索引页面。 用户可以访问存储在会话中的信息,但不能修改它,除非他们拥有密钥,因此您绝不能允许任何人访问您的密钥。 有关详细信息,请参阅 Flask 会话文档 。
密钥应该是一个很长的随机字符串。 您可以使用带有 os.urandom() 方法的 os
模块生成密钥,该方法返回适合加密使用的随机字节字符串。 要使用它获取随机字符串,请打开一个新终端并使用以下命令打开 Python 交互式 shell:
python
在Python交互shell中,从标准库中导入os
模块,调用os.urandom()
方法如下:
import os os.urandom(24).hex()
您将获得类似于以下内容的字符串:
Output 'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'
您可以使用获得的字符串作为您的密钥。
要设置密钥,请通过 app.config
对象将 SECRET_KEY
配置添加到您的应用程序。 在定义 messages
变量之前,将其直接添加到 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()
视图函数修改为如下所示:
烧瓶应用程序/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
将被满足。 如果是这样,则将 'Content is required!'
消息添加到闪烁的消息列表中。
如果正确提交了消息的标题和内容,则使用 messages.append({'title': title, 'content': content})
行将新字典添加到 messages
列表中,其中包含用户提供的标题和内容。 然后使用 redirect()
函数将用户重定向到索引页面。 您使用 url_for()
函数链接到索引页面。
保存并关闭文件。
现在,使用您的网络浏览器导航到 /create
路线:
http://127.0.0.1:5000/create
用您选择的标题和一些内容填写表格。 提交表单后,您将看到索引页面上列出的新消息。
最后,您将显示闪烁的消息并将“新消息”页面的链接添加到 base.html
模板中的导航栏,以便轻松访问这个新页面。 打开基本模板文件:
nano templates/base.html
通过在 <nav>
标签内导航栏中的 FlaskApp 链接后添加新的 <a>
标签来编辑文件。 然后在 content
块正上方添加一个新的 for
循环,以在导航栏下方显示闪烁的消息。 这些消息在 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
路线的“创建”项目。
要查看 Flash 消息的工作原理,请转到“创建”页面,然后单击“提交”按钮而不填写这两个字段。 您将收到如下所示的消息:
返回索引页面,您会看到导航栏下方闪烁的消息消失了,即使它们显示为基本模板的一部分。 如果它们不是闪烁的消息,它们也会显示在索引页面上,因为它也继承自基本模板。
尝试提交带有标题但没有内容的表单。 您将看到消息“需要内容!”。 单击导航栏中的 FlaskApp 链接返回索引页面,然后单击返回按钮返回创建页面。 您会看到消息内容仍然存在。 这仅在您单击“后退”按钮时才有效,因为它会保存先前的请求。 单击导航栏中的创建链接将发送一个新请求,该请求将清除表单,因此闪烁的消息将消失。
您现在知道如何接收用户输入、如何验证它以及如何将其添加到数据源。
注意: 添加到 messages
列表的消息会在服务器停止时消失,因为 Python 列表只保存在内存中,要永久保存您的消息,您需要使用SQLite 之类的数据库。 查看 如何在 Python 3 中使用 sqlite3 模块,了解如何在 Python 中使用 SQLite。
结论
您创建了一个 Flask 应用程序,用户可以在其中将消息添加到索引页面上显示的消息列表中。 您创建了一个 Web 表单,处理了用户通过表单提交的数据,并将其添加到您的消息列表中。 您还使用 Flash 消息在用户提交无效数据时通知他们。
如果您想了解更多关于 Flask 的信息,请查看 Flask 系列中的其他教程。