作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。
介绍
Flask是一个小而轻量级的Python网络框架,提供有用的工具和功能,使在Python中创建Web应用程序更容易。它为开发人员提供了灵活性,并且是新的开发者更容易访问的框架,因为您可以使用单个Python文件快速构建Web应用程序。
作为本教程的一部分,您将使用Bootstrap工具包(https://getbootstrap.com/)来设计您的应用程序,使其更具视觉吸引力。Bootstrap将帮助您将响应式网页纳入您的Web应用程序,以便它在移动浏览器上也很好地工作,而无需写自己的HTML,CSS和JavaScript代码来实现这些目标。
Flask 使用 Jinja 模板引擎以动态地使用熟悉的 Python 概念构建 HTML 页面,如变量、循环、列表等。
在本教程中,您将使用Python中的Flask和SQLite(https://sqlite.org)构建一个小型网页博客。应用程序的用户可以查看您的数据库中的所有帖子,然后点击帖子的标题,以查看其内容,并在数据库中添加新帖子,并编辑或删除现有帖子。
<$>[info] 简化使用 DigitalOcean App Platform的Flask应用程序的部署。
前提条件
在你开始遵循这个指南之前,你将需要:
- 在本地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_blog。
- 对Python 3概念的理解,如 数据类型, 条件声明, 循环, 功能,以及其他类似的概念。
步骤一:安装瓶子
在此步骤中,您将激活 Python 环境并使用 pip
包安装程序安装 Flask。
如果您尚未激活您的编程环境,请确保您位于项目目录中(‘flask_blog’),并使用以下命令激活环境:
1source env/bin/activate
一旦您的编程环境被激活,您的提示现在将有一个env
前缀,可以看起来如下:
此前缀表明环境env
目前处于活跃状态,根据您在创建时如何命名,该环境可能有不同的名称。
注意:您可以使用 Git,一个版本控制系统,以有效地管理和跟踪您的项目的开发过程。
如果您正在使用 Git,最好在您的.gitignore 文件中忽略新创建的 env
目录,以避免跟踪与项目无关的文件。
现在您将安装 Python 包,并将项目代码从主要的 Python 系统安装中分离出来,您将使用pip
和python
来完成此操作。
要安装 Flask,请执行以下命令:
1pip install flask
安装完成后,运行以下命令来确认安装:
1python -c "import flask; print(flask.__version__)"
您可以使用 python
命令行接口和 -c
选项来执行 Python 代码,然后导入 flask
包,然后打印 Flask 版本,这是通过 flask.__version__
变量提供的。
输出将是一个类似于下列版本的版本号:
1[secondary_label Output]
21.1.2
您已经创建了项目文件夹、虚拟环境并安装了 Flask。
步骤二:创建基础应用程序
现在你已经设置了编程环境,你将开始使用Flask. 在这个步骤中,你将创建一个Python文件中的小型Web应用程序,并运行它来启动服务器,该服务器将在浏览器上显示一些信息。
在您的flask_blog
目录中,打开名为hello.py
的文件进行编辑,使用nano
或您最喜欢的文本编辑器:
1nano hello.py
此「hello.py」文件将作为处理 HTTP 请求的最小示例。 里面,你将导入 Flask
对象,并创建一个返回 HTTP 响应的函数。 在「hello.py」里面写下以下代码:
1[label flask_blog/hello.py]
2from flask import Flask
3
4app = Flask(__name__)
5
6@app.route('/')
7def hello():
8 return 'Hello, World!'
在之前的代码块中,你首先从flask
包中导入Flask
对象,然后使用它来创建你的Flask应用程序实例,名为app
。你通过了持有当前Python模块名称的特殊变量__name__
。
一旦你创建了app
实例,你会使用它来处理到来的网页请求,并向用户发送响应。@app.route
是将常规的Python函数转换为Flask _view函数,该函数的返回值转换为由HTTP客户端(如网页浏览器)显示的HTTP响应。
Hello()
视图函数返回字符串Hello, World!
作为响应。
保存并关闭文件。
要运行您的Web应用程序,您首先会告诉Flask在哪里找到应用程序(在您的情况下是hello.py
文件)与FLASK_APP
环境变量:
1export FLASK_APP=hello
然后在开发模式下运行它,使用FLASK_ENV
环境变量:
1export FLASK_ENV=development
最后,使用flask run
命令运行应用程序:
1flask run
一旦应用程序运行,输出将是这样的东西:
1[secondary_label Output]
2 * Serving Flask app "hello" (lazy loading)
3 * Environment: development
4 * Debug mode: on
5 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
6 * Restarting with stat
7 * Debugger is active!
8 * Debugger PIN: 813-894-335
上一个输出有几个信息,如:
- 您正在运行的应用程序的名称.
- 应用程序正在运行的环境.
Debug mode: on
意味着Flask 调试器正在运行. 这是在开发时有用的,因为它会给我们详细的错误消息,当事情发生错误时,这使得解决问题更容易。- 应用程序在URL上本地运行,http://127.0.0.1:5000/`, '127.0.0.1'是代表您的机器的 'localhost' 和 ':5000' 是端口号的 IP。
打开浏览器并键入URL http://127.0.0.1:5000/
,您将收到字符串 Hello, World!
作为回复,这确认您的应用程序正在成功运行。
<$>[警告] 警告 Flask 使用一个简单的 Web 服务器在开发环境中服务我们的应用程序,这也意味着 Flask 调试器正在运行,以便更容易捕捉错误。 该开发服务器不应该用于生产部署。 请参阅 Flask 文档上的 部署选项页面以获取更多信息,您也可以查看此 Flask 部署教程。
现在,您可以离开开发服务器在终端中运行,然后打开另一个终端窗口。 移动到位于hello.py
的项目文件夹,激活虚拟环境,设置环境变量FLASK_ENV
和FLASK_APP
,然后继续到下一个步骤。
<$>[注]
注:在打开新终端时,重要的是要记住激活虚拟环境并设置环境变量 FLASK_ENV
和 FLASK_APP
。
虽然 Flask 应用程序的开发服务器已经运行,但无法使用相同的 flask run
命令运行另一个 Flask 应用程序. 这是因为 flask run
默认使用的端口号码是 5000
,一旦它被打开,它就无法运行另一个应用程序,因此您会收到类似以下的错误:
1[secondary_label Output]
2OSError: [Errno 98] Address already in use
要解决此问题,要么停止当前通过CTRL+C
运行的服务器,然后再次运行flask run
,要么如果要同时运行两个端口,可以将不同的端口号传递给-p
参数,例如,要在端口5001
上运行另一个应用程序,请使用以下命令:
1flask run -p 5001
美元
你现在有一个小型的FlaskWeb应用程序. 你已经运行了你的应用程序,并在Web浏览器上显示了信息。接下来,你将在你的应用程序中使用HTML文件。
第3步:使用HTML模板
目前,您的应用程序只显示一个简单的消息,没有任何HTML。Web应用程序主要使用HTML来显示信息给访问者,所以您现在将致力于将HTML文件纳入您的应用程序中,该文件可以在Web浏览器中显示。
Flask 提供一个 render_template()
辅助功能,允许使用 Jinja 模板引擎。这将通过在 .html
文件中写你的 HTML 代码以及使用你的 HTML 代码中的逻辑来更容易地管理 HTML。你将使用这些 HTML 文件(templates)来构建你的所有应用程序页面,例如你将显示当前博客帖子的主要页面,博客帖的页面,用户可以添加新帖子的页面等等。
在此步骤中,您将在新文件中创建您的主要 Flask 应用程序。
首先,在你的flask_blog
目录中,使用nano
或你最喜欢的编辑器来创建和编辑你的app.py
文件。
1nano app.py
在这个新文件中,你将导入Flask
对象来创建一个Flask应用程序实例,就像你以前一样。你还将导入render_template()
(https://flask.palletsprojects.com/en/1.1.x/api/#flask.render_template)帮助函数,允许你在你要创建的模板
文件夹中渲染存在的HTML模板文件。
1[label flask_blog/app.py]
2from flask import Flask, render_template
3
4app = Flask(__name__)
5
6@app.route('/')
7def index():
8 return render_template('index.html')
index()
视图函数返回了以index.html
为参数呼叫render_template()
的结果,这告诉render_template()
在templates
文件夹中搜索一个名为index.html
的文件。 文件夹和文件都还不存在,如果你在这个时候运行应用程序,你会收到一个错误。
保存和退出文件。
在您的其他终端中停止开发服务器,该服务器以CTRL+C运行你好
应用程序。
在运行应用程序之前,请确保正确指定环境变量FLASK_APP
的值,因为您不再使用应用程序Hello
:
1export FLASK_APP=app
2flask run
在您的浏览器中打开URL http://127.0.0.1:5000/
将导致调试页面告知您没有找到template index.html
模板。 代码中的主要行将突出显示这个错误。 在这种情况下,它是‘return render_template('index.html')’。
如果您点击此行,调试程序将显示更多的代码,以便您有更多的背景,以帮助您解决问题。
要修复此错误,请在您的flask_blog
目录中创建名为模板
的目录,然后在其内部打开名为index.html
的文件以进行编辑:
1mkdir templates
2nano templates/index.html
接下来,在 index.html 中添加以下 HTML 代码:
1[label flask_blog/templates/index.html]
2<!DOCTYPE html>
3<html lang="en">
4<head>
5 <meta charset="UTF-8">
6 <title>FlaskBlog</title>
7</head>
8<body>
9 <h1>Welcome to FlaskBlog</h1>
10</body>
11</html>
保存文件并使用您的浏览器再次导航到 http://127.0.0.1:5000/
,或刷新页面. 此时,浏览器应该在一个 <h1>
标签中显示文本 Welcome to FlaskBlog
。
除了模板
文件夹外,Flask Web 应用程序通常还具有用于托管静态文件的静态
文件夹,例如 CSS 文件、JavaScript 文件和应用程序使用的图像。
您可以创建一个style.css
风格表文件,将CSS添加到您的应用程序中,首先,在您的主要flask_blog
目录中创建一个名为static
的目录:
1mkdir static
然后在静态
目录中创建另一个名为css
的目录,以托管.css
文件。这通常是为了在专用文件夹中组织静态文件,因此,JavaScript文件通常生活在名为js
的目录中,图像被放入名为images
(或img
)的目录中,等等。
1mkdir static/css
然后在css
目录中打开一个style.css
文件来编辑:
1nano static/css/style.css
将以下 CSS 规则添加到您的 style.css 文件中:
1[label flask_blog/static/css/style.css]
2h1 {
3 border: 2px #eee solid;
4 color: brown;
5 text-align: center;
6 padding: 10px;
7}
CSS 代码将添加边界,将颜色更改为棕色,中心文本,并添加<h1>
标签。
保存并关闭文件。
接下来,打开index.html
模板文件进行编辑:
1nano templates/index.html
您将添加一个链接到style.css
文件在index.html
模板文件的<head>
部分:
1[label flask_blog/templates/index.html]
2. . .
3<head>
4 <meta charset="UTF-8">
5 <link rel="stylesheet" href="{{ url_for('static', filename= 'css/style.css') }}">
6 <title>FlaskBlog</title>
7</head>
8. . .
在这里,您使用 url_for()
辅助函数来生成文件的适当位置. 第一个参数表示您正在链接到静态文件,第二个参数是静态目录中的文件路径。
保存并关闭文件。
当您更新应用程序的索引页面时,您会注意到文本欢迎来到FlaskBlog
现在是棕色,中心,并在边界中封闭。
但是,如果你不是网页设计师,或者如果你不熟悉CSS,那么你可以使用Bootstrap工具包(https://getbootstrap.com/),它为你的应用程序提供易于使用的元件。
您可能已经猜测,创建另一个 HTML 模板意味着重复您已经在 index.html 模板中写的大部分 HTML 代码. 您可以通过一个 base template 文件来避免不必要的代码重复,您所有的 HTML 文件都会继承。
要创建一个基础模板,首先在你的模板
目录中创建一个名为base.html
的文件:
1nano templates/base.html
在您的「base.html」模板中输入以下代码:
1[label flask_blog/templates/base.html]
2<!doctype html>
3<html lang="en">
4 <head>
5 <!-- Required meta tags -->
6 <meta charset="utf-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8
9 <!-- Bootstrap CSS -->
10 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
11
12 <title>{% block title %} {% endblock %}</title>
13 </head>
14 <body>
15 <nav class="navbar navbar-expand-md navbar-light bg-light">
16 <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
17 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
18 <span class="navbar-toggler-icon"></span>
19 </button>
20 <div class="collapse navbar-collapse" id="navbarNav">
21 <ul class="navbar-nav">
22 <li class="nav-item active">
23 <a class="nav-link" href="#">About</a>
24 </li>
25 </ul>
26 </div>
27 </nav>
28 <div class="container">
29 {% block content %} {% endblock %}
30 </div>
31
32 <!-- Optional JavaScript -->
33 <!-- jQuery first, then Popper.js, then Bootstrap JS -->
34 <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
35 <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
36 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
37 </body>
38</html>
保存并关闭文件,一旦完成编辑。
在前一块中大部分的代码是标准的HTML和Bootstrap所需的代码. <meta>
标签为Web浏览器提供信息, <link>
标签链接到Bootstrap CSS文件,而 <script>
标签是链接到JavaScript代码,允许一些额外的Bootstrap功能。
但是,以下突出部分是Jinja模板引擎的特征:
{% block title %} {% endblock %}
: 作为一个标题的位置持有人(https://jinja.palletsprojects.com/en/2.10.x/templates/#blocks)的区块,你以后会用它在其他模板中给你的应用程序中的每个页面一个自定义标题而不重写整个<head>
部分每次。{ url_for('index')}}: 一个函数调用,将返回 URL for the
index()视图函数. 这与以前的
url_for()` 调用你用来链接一个静态 CSS 文件不同,因为它只需要一个参数,即视图函数的名称,并链接到与该函数相关的路径而不是静态文件。
现在你有一个基础模板,你可以利用继承来利用它。
1nano templates/index.html
然后将其内容替换为如下:
1[label flask_blog/templates/index.html]
2{% extends 'base.html' %}
3
4{% block content %}
5 <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
6{% endblock %}
在这个index.html
模板的新版本中,您使用{% extends %}
标签来继承从base.html
模板中。
这个内容
块包含一个<h1>
标签,在一个标题
块中包含文本欢迎到FlaskBlog
,这反过来会用文本欢迎到FlaskBlog
代替base.html
模板中的原始标题
块。
模板继承还允许您在其他模板中重复使用HTML代码(在这种情况下是base.html),而无需每次重复使用。
保存和关闭文件,并在您的浏览器上更新索引页面. 您将看到您的页面与导航栏和风格的标题。
您已经在 Flask 中使用了 HTML 模板和静态文件,您还使用 Bootstrap 开始改进页面外观和基础模板,以避免代码重复。
步骤4:创建数据库
在此步骤中,您将设置一个数据库来存储数据,即您的应用程序的博客帖子。
您将使用 SQLite 数据库文件来存储您的数据,因为 sqlite3
模块,我们将用来与数据库进行交互,可以在标准的 Python 库中找到。
首先,因为SQLite中的数据存储在表和列中,并且由于您的数据主要由博客帖子组成,您首先需要创建一个名为帖子
的表,其中包含所需的列。
在您的「flask_blog」目錄中開啟名為「schema.sql」的檔案:
1nano schema.sql
在此文件中输入以下 SQL 命令:
1[label flask_blog/schema.sql]
2DROP TABLE IF EXISTS posts;
3
4CREATE TABLE posts (
5 id INTEGER PRIMARY KEY AUTOINCREMENT,
6 created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
7 title TEXT NOT NULL,
8 content TEXT NOT NULL
9);
保存并关闭文件。
第一个 SQL 命令是DROP TABLE IF EXISTS posts;
,这会删除任何已经存在的名为posts
的表格,这样你就不会出现混淆的行为。 请注意,这将删除你在数据库中的所有内容,每次你使用这些 SQL 命令,所以确保你不会在 Web 应用程序中写任何重要的内容,直到你完成本教程并尝试最终结果。
id
: 代表一个 primary key 的整数,这将被数据库分配给每个条目(即博客帖子)的唯一值。created
: 该博客帖创建时间在.NOT NULL
意味着该列不应该空,而DEFAULT
值是CURRENT_TIMESTAMP
值,即该帖子被添加到数据库的时间。
现在你在schema.sql
文件中有一个 SQL 方案,你将使用它创建数据库,使用 Python 文件生成 SQLite `.db’ 数据库文件。
1nano init_db.py
然后添加以下代码。
1[label flask_blog/init_db.py]
2import sqlite3
3
4connection = sqlite3.connect('database.db')
5
6with open('schema.sql') as f:
7 connection.executescript(f.read())
8
9cur = connection.cursor()
10
11cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
12 ('First Post', 'Content for the first post')
13 )
14
15cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
16 ('Second Post', 'Content for the second post')
17 )
18
19connection.commit()
20connection.close()
你首先导入sqlite3
模块,然后打开一个连接到名为database.db
的数据库文件,它将在运行Python文件后创建,然后使用open()
函数打开schema.sql
文件,然后使用executescript()
(https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.executescript)方法执行其内容,同时执行多个SQL陈述,这将创建帖子
表,然后创建一个Cursor object
(https://docs.python.org/3/library/sqlite3.html#cursor-objects),允许你使用它的execute()
(https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.execute)方法执行两个INSERT
SQL陈述,将两个博客帖子添加到你的帖子
表。
保存和关闭文件,然后使用python
命令在终端运行它:
1python init_db.py
一旦该文件完成执行,一个名为database.db
的新文件将出现在您的flask_blog
目录中,这意味着您已经成功设置了您的数据库。
在下一步中,您将获取您插入数据库的帖子,并在应用程序的主页中显示它们。
步骤 5 - 显示所有帖子
现在你已经设置了数据库,你现在可以修改index()
视图函数,以显示你在数据库中的所有帖子。
打开app.py
文件以进行以下更改:
1nano app.py
对于您的第一个修改,您将导入文件顶部的 sqlite3
模块:
1[label flask_blog/app.py]
2import sqlite3
3from flask import Flask, render_template
4
5. . .
接下来,您将创建一个函数,创建数据库连接并返回它。
1[label flask_blog/app.py]
2. . .
3from flask import Flask, render_template
4
5def get_db_connection():
6 conn = sqlite3.connect('database.db')
7 conn.row_factory = sqlite3.Row
8 return conn
9
10. . .
此 get_db_connection()
函数打开对 database.db
数据库文件的连接,然后将 row_factory
属性设置为 sqlite3.Row
,以便您可以基于名称访问列。
定义get_db_connection()
函数后,修改index()
函数以显示如下:
1[label flask_blog/app.py]
2. . .
3
4@app.route('/')
5def index():
6 conn = get_db_connection()
7 posts = conn.execute('SELECT * FROM posts').fetchall()
8 conn.close()
9 return render_template('index.html', posts=posts)
在这个新版本的index()
函数中,您首先使用您之前定义的get_db_connection()
函数打开数据库连接,然后运行 SQL 查询,从帖子
表中选择所有条目。
您使用 close()
方法关闭数据库连接并返回index.html
模板的渲染结果. 您还将帖子
对象作为一个参数,其中包含您从数据库中获得的结果,这将允许您访问index.html
模板中的博客帖子。
有了这些更改,保存并关闭app.py
文件。
现在你已经将从数据库中提取的帖子传递到‘index.html’模板中,你可以使用一个 for
loop来显示每个帖子在你的索引页面上。
打开index.html
文件:
1nano templates/index.html
然后,修改它看起来如下:
1[label flask_blog/templates/index.html]
2{% extends 'base.html' %}
3
4{% block content %}
5 <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
6 {% for post in posts %}
7 <a href="#">
8 <h2>{{ post['title'] }}</h2>
9 </a>
10 <span class="badge badge-primary">{{ post['created'] }}</span>
11 <hr>
12 {% endfor %}
13{% endblock %}
在这里,语法 {% for post in %}' 是一个 Jinja
for循环,类似于 Python
for循环,但它必须后来用
{% endfor %}' 语法关闭。你使用这个语法在字符串 index() 函数在行
return render_template('index.html', posts=posts)中通过的
posts' 列表中的每个项目上循环。
您使用字面变量分界器({... }})显示标题。 请记住,
帖子将是一个字典类似的对象,因此您可以使用
帖子标题`访问帖子标题。
一旦你完成了编辑文件,保存并关闭它,然后导航到你的浏览器的索引页面,你会看到你在你的页面上添加到数据库的两个帖子。
现在你已经修改了index()
视图功能,以显示你在应用程序的主页上数据库中的所有帖子,你将继续以单一页面显示每个帖子,并允许用户链接到每个单独的帖子。
步骤 6 - 显示单个帖子
在此步骤中,您将创建一个新的Flask路线,具有视图功能和一个新的HTML模板,以通过其ID显示单个博客帖子。
在此步骤结束时,URL http://127.0.0.1:5000/1
将是一个显示第一个帖子的页面(因为它具有ID 1
)。
打开app.py
来编辑:
1nano app.py
由于您将在本项目后期需要从数据库中从多个位置获取博客帖子以其ID,您将创建一个名为get_post()
的独立函数,您可以通过将其称为ID并收到与所提供ID相关的博客帖子,或者让Flask以404 Not Found
消息回复,如果博客帖子不存在。
要用「404」页面回复,您需要从安装在文件顶部的「Werkzeug(LINK1)」库中导入 abort()
函数:
1[label flask_blog/app.py]
2import sqlite3
3from flask import Flask, render_template
4from werkzeug.exceptions import abort
5
6. . .
然后,在上一个步骤中创建的get_db_connection()函数之后,添加get_post()
函数:
1[label flask_blog/app.py]
2. . .
3
4def get_db_connection():
5 conn = sqlite3.connect('database.db')
6 conn.row_factory = sqlite3.Row
7 return conn
8
9def get_post(post_id):
10 conn = get_db_connection()
11 post = conn.execute('SELECT * FROM posts WHERE id = ?',
12 (post_id,)).fetchone()
13 conn.close()
14 if post is None:
15 abort(404)
16 return post
17
18. . .
这个新函数有一个post_id
参数,决定要返回哪个博客帖子。
在函数中,您使用get_db_connection()
函数打开数据库连接并执行 SQL 查询以获取与给定的post_id
值相关联的博客帖子。您添加了fetchone()
方法以获取结果并将其存储在post
变量中,然后关闭了连接。 如果post
变量具有None
值,这意味着数据库中没有发现结果,您使用您之前导入的abort()
函数以使用404
错误代码响应,并且该函数将完成执行。
接下来,在app.py
文件的末尾添加以下视图函数:
1[label flask_blog/app.py]
2. . .
3
4@app.route('/<int:post_id>')
5def post(post_id):
6 post = get_post(post_id)
7 return render_template('post.html', post=post)
在这个新视图函数中,您添加了 variable rule <int:post_id>
来指定下落后的部分(/
)是一个正整数(标记为 int
转换器),您需要在视图函数中访问。
保存app.py
文件并打开一个新的post.html
模板文件进行编辑:
1nano templates/post.html
在这个新的post.html
文件中输入以下代码,它将类似于index.html
文件,但它只会显示一个帖子,除此之外还会显示帖子的内容:
1[label flask_blog/templates/post.html]
2{% extends 'base.html' %}
3
4{% block content %}
5 <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
6 <span class="badge badge-primary">{{ post['created'] }}</span>
7 <p>{{ post['content'] }}</p>
8{% endblock %}
您可以添加您在base.html
模板中定义的标题
块,以使页面标题同时反映在<h2>
标题中显示的帖子标题。
保存并关闭文件。
您现在可以导航到以下URL,查看您在数据库中的两个帖子,以及告诉用户请求的博客帖子没有被找到的页面(因为迄今为止没有3
ID号码的帖子):
1http://127.0.0.1:5000/1
2http://127.0.0.1:5000/2
3http://127.0.0.1:5000/3
回到索引页面,你会将每个帖子标题链接到其相应的页面。你会使用url_for()
函数来做到这一点。首先,打开index.html
模板进行编辑:
1nano templates/index.html
然后,将 href 属性的值从 # 更改为{{ url_for('post', post_id=post['id']) }},以便为的循环看起来如下:
1[label flask_blog/templates/index.html]
2{% for post in posts %}
3 <a href="{{ url_for('post', post_id=post['id']) }}">
4 <h2>{{ post['title'] }}</h2>
5 </a>
6 <span class="badge badge-primary">{{ post['created'] }}</span>
7 <hr>
8{% endfor %}
在这里,您将post
转移到url_for()
函数作为第一个参数,这是post()
视图函数的名称,并且因为它接受了post_id
参数,因此您会给它post['id]
值。
保存并关闭文件。
索引页面上的链接现在将按预期运作。 通过此,您现在已经完成了负责显示博客帖子的应用程序部分的构建,然后您将添加创建,编辑和删除博客帖子的能力。
第7步:修改文章
现在您已经完成了在 Web 应用程序中的数据库中存在的博客帖子的显示,您需要允许应用程序的用户编写新的博客帖子,并将其添加到数据库中,编辑现有的文章,并删除不必要的博客帖子。
创建新邮件
到目前为止,你有一个应用程序,显示了数据库中的帖子,但没有办法添加新帖子,除非你直接连接到SQLite数据库并手动添加一个帖子。
打开app.py
文件来编辑:
1nano app.py
首先,您将从 Flask 框架中导入以下内容:
将导入文档添加到您的文件中如下:
1[label flask_blog/app.py]
2import sqlite3
3from flask import Flask, render_template, request, url_for, flash, redirect
4from werkzeug.exceptions import abort
5
6. . .
这个秘密密密钥用于保护会话,使Flask能够记住从一个请求到另一个请求的信息,例如从新邮件页面移动到索引页面。用户可以访问会话中存储的信息,但除非他们有秘密密密钥,否则无法更改它,因此您绝不能允许任何人访问您的秘密密密钥。
要设置 secret key,您将通过app.config
对象将SECRET_KEY
配置添加到您的应用程序中,然后在定义index()
视图函数之前直接按照app
定义添加它:
1[label flask_blog/app.py]
2. . .
3app = Flask(__name__)
4app.config['SECRET_KEY'] = 'your secret key'
5
6@app.route('/')
7def index():
8 conn = get_db_connection()
9 posts = conn.execute('SELECT * FROM posts').fetchall()
10 conn.close()
11 return render_template('index.html', posts=posts)
12
13. . .
请记住,秘密密钥应该是一个长的随机字符串。
设置秘密密密钥后,您将创建一个视图函数,该函数将显示一个表单,您可以填写以创建一个新的博客帖子。
1[label flask_blog/app.py]
2. . .
3
4@app.route('/create', methods=('GET', 'POST'))
5def create():
6 return render_template('create.html')
这会创建一个接受 GET 和 POST 请求的 /create' 路径. 默认情况下,GET 请求被接受. 为了也接受 POST 请求,这些请求由浏览器在提交表单时发送,你会将接受类型的请求的 [tuple](https://andsky.com/tech/tutorials/understanding-tuples-in-python-3)传递给
@app.route()装饰器的
methods` 参数。
保存并关闭文件。
要创建模板,请在您的模板
文件夹中打开名为create.html
的文件:
1nano templates/create.html
将以下代码添加到新文件中:
1[label flask_blog/templates/create.html]
2{% extends 'base.html' %}
3
4{% block content %}
5<h1>{% block title %} Create a New Post {% endblock %}</h1>
6
7<form method="post">
8 <div class="form-group">
9 <label for="title">Title</label>
10 <input type="text" name="title"
11 placeholder="Post title" class="form-control"
12 value="{{ request.form['title'] }}"></input>
13 </div>
14
15 <div class="form-group">
16 <label for="content">Content</label>
17 <textarea name="content" placeholder="Post content"
18 class="form-control">{{ request.form['content'] }}</textarea>
19 </div>
20 <div class="form-group">
21 <button type="submit" class="btn btn-primary">Submit</button>
22 </div>
23</form>
24{% endblock %}
这个代码的大部分是标准的HTML,它会显示帖子标题的输入框,帖子内容的文本区域,以及提交表单的按钮。
帖子标题输入的值是 {{ request.form['title'] }}
,文本区域有 `{{ request.form['content'] }}'的值,这样做是这样,如果你输入的数据如果有什么不对劲,就不会丢失。例如,如果你写了一篇长篇文章而忘记给它一个标题,会显示一个信息告诉你标题是必要的。
现在,随着开发服务器运行,使用您的浏览器导航到‘/create’路径:
1http://127.0.0.1:5000/create
您将看到一个 创建新帖子页面,其中有一个标题和内容的框。
此表单将 POST 请求提交到创建()
视图函数中,但该函数中尚无处理 POST 请求的代码,因此在填写和提交表单后不会发生任何情况。
当表单提交时,您将处理传入的 POST 请求,您将在创建()
视图中执行此操作,您可以单独处理 POST 请求,通过检查请求.方法
的值。
打开app.py
文件来编辑:
1nano app.py
更改创建()
视图函数以显示如下:
1[label flask_blog/app.py]
2. . .
3
4@app.route('/create', methods=('GET', 'POST'))
5def create():
6 if request.method == 'POST':
7 title = request.form['title']
8 content = request.form['content']
9
10 if not title:
11 flash('Title is required!')
12 else:
13 conn = get_db_connection()
14 conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
15 (title, content))
16 conn.commit()
17 conn.close()
18 return redirect(url_for('index'))
19
20 return render_template('create.html')
在如果
声明中,您可以确保仅当请求是 POST 请求时,通过比较 `request.method == 'POST' 执行随后的代码。
然后,您将提交的标题和内容从request.form
对象中提取,该对象允许您访问请求中的表单数据。如果没有提供标题,则将满足如果不是标题
的条件,向用户显示信息,通知他们需要标题。
然后,您将对数据库进行更改,并关闭连接. 在将博客文章添加到数据库后,您将客户端重定向到索引页面,使用重定向()
函数,将由url_for()
函数生成的 URL 传递给该客户端,以值``索引`作为参数。
保存并关闭文件。
现在,使用您的网页浏览器导航到创建
路径:
1http://127.0.0.1:5000/create
填写表格以您选择的标题和一些内容. 一旦您提交表单,您将看到新的帖子列在索引页面上。
最后,你会显示闪烁的消息,并在base.html
模板中添加导航栏的链接,以便轻松访问这个新页面。
1nano templates/base.html
通过在
1[label flask_blog/templates/base.html]
2<nav class="navbar navbar-expand-md navbar-light bg-light">
3 <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
4 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
5 <span class="navbar-toggler-icon"></span>
6 </button>
7 <div class="collapse navbar-collapse" id="navbarNav">
8 <ul class="navbar-nav">
9 <li class="nav-item">
10 <a class="nav-link" href="#">About</a>
11 </li>
12 <li class="nav-item">
13 <a class="nav-link" href="{{url_for('create')}}">New Post</a>
14 </li>
15 </ul>
16 </div>
17</nav>
18<div class="container">
19 {% for message in get_flashed_messages() %}
20 <div class="alert alert-danger">{{ message }}</div>
21 {% endfor %}
22 {% block content %} {% endblock %}
23</div>
保存和关闭文件. 导航栏现在将有一个新邮件
项目,链接到 / 创建
路径。
编辑一个帖子
为了一个博客是最新的,你需要能够编辑你的现有帖子. 本节将引导你通过创建一个新的页面在你的应用程序,以简化编辑一个帖子的过程。
首先,您将添加一个新的路径到 app.py
文件. 它的视图函数将收到需要编辑的帖子的 ID,URL将以 /post_id/edit
格式,而 post_id
变量是帖子的 ID。
1nano app.py
然后在文件末尾添加以下edit()
视图函数. 编辑现有帖子类似于创建一个新的帖子,所以这个视图函数将类似于create()
视图函数:
1[label flask_blog/app.py]
2. . .
3
4@app.route('/<int:id>/edit', methods=('GET', 'POST'))
5def edit(id):
6 post = get_post(id)
7
8 if request.method == 'POST':
9 title = request.form['title']
10 content = request.form['content']
11
12 if not title:
13 flash('Title is required!')
14 else:
15 conn = get_db_connection()
16 conn.execute('UPDATE posts SET title = ?, content = ?'
17 ' WHERE id = ?',
18 (title, content, id))
19 conn.commit()
20 conn.close()
21 return redirect(url_for('index'))
22
23 return render_template('edit.html', post=post)
您编辑的帖子由 URL 确定,Flask 将 ID 号码通过 id
参数传递给 edit()
函数. 您将此值添加到 get_post()
函数,以从数据库中获取与所提供的 ID 相关的帖子。
就像创建新帖子一样,你首先从request.form
对象中提取数据,然后闪烁消息,如果标题有空值,否则你打开数据库连接。
在 GET 请求的情况下,您会将一个edit.html
模板转移到post
变量中,该变量包含get_post()
函数的返回值,您将使用此功能在编辑页面上显示现有的标题和内容。
保存并关闭文件,然后创建一个新的edit.html 模板:
1nano templates/edit.html
在这个新文件中写下以下代码:
1[label flask_blog/templates/edit.html]
2{% extends 'base.html' %}
3
4{% block content %}
5<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
6
7<form method="post">
8 <div class="form-group">
9 <label for="title">Title</label>
10 <input type="text" name="title" placeholder="Post title"
11 class="form-control"
12 value="{{ request.form['title'] or post['title'] }}">
13 </input>
14 </div>
15
16 <div class="form-group">
17 <label for="content">Content</label>
18 <textarea name="content" placeholder="Post content"
19 class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
20 </div>
21 <div class="form-group">
22 <button type="submit" class="btn btn-primary">Submit</button>
23 </div>
24</form>
25<hr>
26{% endblock %}
保存并关闭文件。
此代码遵循相同的模式,除了{{ request.form['title'] 或 post['title'] }}
和{{ request.form['content']或 post['content'] }}
语法之外。
现在,导航到以下URL来编辑第一个帖子:
1http://127.0.0.1:5000/1/edit
你会看到一个 ** 编辑第一个帖子
**页面。
编辑帖子并提交表格,然后确保帖子更新。
现在您需要添加一个链接,指向索引页面的每个帖子的编辑页面。
1nano templates/index.html
编辑文件以完全看起来如下:
1[label flask_blog/templates/index.html]
2{% extends 'base.html' %}
3
4{% block content %}
5 <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
6 {% for post in posts %}
7 <a href="{{ url_for('post', post_id=post['id']) }}">
8 <h2>{{ post['title'] }}</h2>
9 </a>
10 <span class="badge badge-primary">{{ post['created'] }}</span>
11 <a href="{{ url_for('edit', id=post['id']) }}">
12 <span class="badge badge-warning">Edit</span>
13 </a>
14 <hr>
15 {% endfor %}
16{% endblock %}
保存并关闭文件。
在这里,您添加一个<a>
标签链接到edit()
视图函数,通过post['id]
值链接到每个帖子的编辑页面,并使用Edit
链接。
删除一个帖子
有时一个帖子不再需要公开可用,这就是为什么删除帖子的功能至关重要。
首先,你会添加一个新的/ID/delete
路径,它接受POST请求,类似于edit()
视图函数。你的新的delete()
视图函数将收到要从URL中删除的帖子的ID。
1nano app.py
在文件底部添加以下视图函数:
1[label flask_blog/app.py]
2# ....
3
4@app.route('/<int:id>/delete', methods=('POST',))
5def delete(id):
6 post = get_post(id)
7 conn = get_db_connection()
8 conn.execute('DELETE FROM posts WHERE id = ?', (id,))
9 conn.commit()
10 conn.close()
11 flash('"{}" was successfully deleted!'.format(post['title']))
12 return redirect(url_for('index'))
此视图功能只接受 POST 请求,这意味着导航到浏览器上的 `/ID/delete' 路径会返回错误,因为 Web 浏览器默认情况下 GET 请求。
但是,您可以通过发送一个 POST 请求的表单访问此路径,该函数将收到该 ID 值,并使用该函数以 get_post()
函数从数据库中获取邮件。
然后您打开数据库连接并执行一个DELETE FROM
SQL 命令来删除邮件. 您将对数据库进行更改,并在闪烁消息时关闭连接,以告知用户邮件已成功删除,并将其重定向到索引页面。
请注意,您不会渲染模板文件,这是因为您只会将删除
按钮添加到编辑页面。
打开edit.html
模板文件:
1nano templates/edit.html
然后在<hr>
标签后并直接在{%终端区块 %}
行前添加下面的<form>
标签:
1[label flask_blog/templates/edit.html]
2<hr>
3
4<form action="{{ url_for('delete', id=post['id']) }}" method="POST">
5 <input type="submit" value="Delete Post"
6 class="btn btn-danger btn-sm"
7 onclick="return confirm('Are you sure you want to delete this post?')">
8</form>
9
10{% endblock %}
您使用confirm()
方法在提交请求之前显示确认消息。
现在再次导航到博客文章的编辑页面,并尝试删除它:
1http://127.0.0.1:5000/1/edit
在此步骤结束时,项目的源代码将看起来像 本页上的代码。
有了这个,应用程序的用户现在可以写新的博客帖子,并将其添加到数据库中,编辑和删除现有帖子。
结论
本教程介绍了Flask Python框架的基本概念。您学会了如何创建一个小型Web应用程序,在开发服务器中运行它,并允许用户通过URL参数和Web表格提供自定义数据。您还使用了Jinja模板引擎(http://jinja.palletsprojects.com/)来重复使用HTML文件并在其中使用逻辑。本教程的末尾,您现在有一个完全功能的Web博客,它与SQLite数据库(https://sqlite.org/)互动,使用Python语言和SQL查询创建,显示,编辑和删除博客帖子。如果您想了解更多有关Flask和SQLite的工作,请参阅本教程(How To Use One-to-Many Database Relations with Flask and SQLite)(https://andsky.com/tech/tutorials/how-to-use-one-to-many-database-relationships-with-flask-and-sqlite)。
您可以通过添加用户身份验证来进一步开发此应用程序,这样只有注册用户才能创建和修改博客帖子,您也可以为每个博客帖子添加评论和标签,并添加文件上传,让用户能够在帖子中包含图像。
Flask 有许多社区制作的 Flask 扩展)。以下是您可能考虑使用的扩展列表,以便您更轻松地开发过程:
- Flask-Login:管理用户会话,处理登录和登录,并记住登录的用户。
- Flask-SQLAlchemy:简化使用Flask与 SQLAlchemy,Python SQL工具包和对象关系地图器与SQL数据库互动。