如何在 Flask-SQLAlchemy 中使用一对多数据库关系

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

介绍

Flask是一个轻量级的Python网络框架,提供用于在Python语言中创建Web应用程序的有用工具和功能。 SQLAlchemy是一个SQL工具包,为关系数据库提供高效和高性能的数据库访问。它提供了与诸如SQLite、MySQL和PostgreSQL等多个数据库引擎进行交互的方式。它为您提供数据库的SQL功能。它还为您提供了一个Object Relational Mapper(ORM),允许您使用简单的Python对象和方法进行查询和处理数据。 Flask-SQLAlchemy是一个Flask扩展,使使用SQLAlchemy与Flask更容易,为您提供工具和方法来与您的Flask应用程序中的数据库

一个对多个数据库关系是两个数据库表之间的关系,其中一个表中的记录可以引用另一个表中的多个记录。例如,在博客应用程序中,存储帖的表可以与存储评论的表有一个对多个关系。每个帖子可以引用多个评论,每个评论引用一个单一的帖子;因此,一个帖子有一个与许多评论的关系。

在本教程中,您将构建一个小型博客系统,展示如何使用Flask-SQLAlchemy扩展构建一对多关系。

前提条件

步骤 1 — 安装 Flask 和 Flask-SQLAlchemy

在此步骤中,您将为您的应用程序安装所需的包。

當您的虛擬環境啟用時,使用「pip」來安裝 Flask 和 Flask-SQLAlchemy:

1pip install Flask Flask-SQLAlchemy

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

1[secondary_label Output]
2Successfully installed Flask-2.1.1 Flask-SQLAlchemy-2.5.1 Jinja2-3.1.1 MarkupSafe-2.1.1 SQLAlchemy-1.4.35 Werkzeug-2.1.1 click-8.1.2 greenlet-1.1.2 itsdangerous-2.1.2

安装所需的 Python 包后,您将下一步设置数据库。

步骤 2 – 设置数据库和模型

在此步骤中,您将设置您的数据库,并创建 SQLAlchemy database models — Python 类,代表您的数据库表. 您将创建博客帖子的模型和评论模型. 您将启动数据库,创建帖子的表,并根据您宣布的模型添加评论表。

创建数据库连接

在您的flask_app目录中打开名为app.py的文件. 此文件将包含设置数据库和您的Flask路径的代码:

1nano app.py

此檔案將連接到名為「database.db」的 SQLite 資料庫,並有兩個類別:一個名為「Post」的類別,代表您的資料庫發表表表表,以及一個代表評論表的「評論」類別。

1[label flask_app/app.py]
2import os
3from flask import Flask, render_template, request, redirect, url_for
4from flask_sqlalchemy import SQLAlchemy

在这里,您将导入 os 模块,它为您提供访问不同的操作系统界面,您将使用它来构建您的 `database.db' 数据库文件的文件路径。

flask包中,您可以导入应用程序所需的必要辅助工具:用于创建 Flask 应用程序实例的 Flask 类,用于渲染模板的 render_template() 函数,用于处理请求的 request 对象,用于构建路径 URL 的 url_for() 函数,以及用于重定向用户的 redirect() 函数。

然后,您从Flask-SQLAlchemy扩展中导入SQLAlchemy类,该类允许您访问 SQLAlchemy的所有函数和类,除了助手和集成Flask与SQLAlchemy的功能,您将使用它创建一个连接到Flask应用程序的数据库对象,允许您使用Python类、对象和功能创建和操纵表,而无需使用SQL语言。

在导入下方,您将设置数据库文件路径,实时化您的 Flask 应用程序,并配置和连接您的应用程序到 SQLAlchemy。

 1[label flask_app/app.py]
 2
 3basedir = os.path.abspath(os.path.dirname(__file__))
 4
 5app = Flask(__name__)
 6app.config['SQLALCHEMY_DATABASE_URI'] =\
 7           'sqlite:///' + os.path.join(basedir, 'database.db')
 8app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
 9
10db = SQLAlchemy(app)

在这里,您为您的 SQLite 数据库文件构建一条路径. 您首先将基础目录定义为当前目录. 您使用 os.path.abspath() 函数获取当前文件目录的绝对路径. 特殊的 __file__ 变量具有当前 app.py 文件的路径名称. 您将基础目录的绝对路径存储在一个名为 basedir 的变量中。

然后创建一个名为app的Flask应用程序实例,您可以使用它来配置两个Flask-SQLAlchemy 配置密钥:

  • SQLALCHEMY_DATABASE_URI: 数据库 URI 用于指定您想要建立连接的数据库。在这种情况下, URI 会按照格式 sqlite:///path/to/database.db 使用 os.path.join() 函数来智能地连接您在 basedir 变量中构建和存储的数据库目录,以及 database.db 文件名。 这将连接到您的 flask_app 目录中的 database.db 数据库文件。 该文件将在您启动数据库后创建。
  • SQL ALCHEMY_TRACK_MIFIFICATIONS: 一个配置允许或禁用对象的跟踪

<$>[注] 注: 如果您想使用其他数据库引擎,如 PostgreSQL 或 MySQL,则需要使用适当的 URI。

对于 PostgreSQL,使用以下格式:

1postgresql://username:password@host:port/database_name

对于思维:

1mysql://username:password@host:port/database_name

有关更多信息,请参阅 SQLAlchemy 引擎配置文档

通过设置数据库 URI 并禁用跟踪来配置 SQLAlchemy 后,您使用SQLAlchemy类创建数据库对象,通过应用程序实例将您的 Flask 应用程序连接到 SQLAlchemy。

宣告桌子

有了建立的数据库连接和创建的数据库对象,您将使用数据库对象创建一个数据库表的帖子和一个数据库表的评论. 表由一个 model 表示 - 一个 Python 类继承从一个基类 Flask-SQLAlchemy 通过您之前创建的 db 数据库实例提供。

 1[label flask_app/app.py]
 2
 3class Post(db.Model):
 4    id = db.Column(db.Integer, primary_key=True)
 5    title = db.Column(db.String(100))
 6    content = db.Column(db.Text)
 7    comments = db.relationship('Comment', backref='post')
 8
 9    def __repr__(self):
10        return f'<Post "{self.title}">'
11
12class Comment(db.Model):
13    id = db.Column(db.Integer, primary_key=True)
14    content = db.Column(db.Text)
15    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
16
17    def __repr__(self):
18        return f'<Comment "{self.content[:20]}...">'

在这里,您创建一个帖子模型和一个评论模型,这些模型继承了db.Model类。

Post 模型代表邮件表. 您使用 db.Column` 类来定义其列. 第一个参数代表列类型,其他参数代表列配置。

您为邮件模型定义了以下列:

  • id:邮件ID. 您将其定义为一个整数,使用 db.Integer. primary_key=True 将这个列定义为 primary key,这将为每个条目(即每个条目)由数据库分配一个独特的值。
  • title: 邮件的标题. 最长100个字符的字符串.
  • content: 邮件的内容。

评论类属性定义了帖子模型与评论模型之间的一对多关系。您使用db.relationship()方法,将其称为评论模型(在这种情况下是评论)参数。您使用backref参数添加到评论模型中行为像一列的回归参数。这样,您可以使用帖子属性访问所发布的评论。例如,如果您有一个名为评论的变量中的评论对象,您将能够访问使用评论邮件所属的评论。稍后您将看到一个示例。

请参阅 SQLAlchemy 文档 列类型,而不是您在上一个代码块中使用的类型。

特殊的 __repr__函数允许您给每个对象一个字符串表示来识别它用于调试目的。

评论模型代表评论表,您可以为其定义以下列:

  • id: 评论ID. 您将其定义为 db.Integer 的整数。 primary_key=True 将此列定义为 primary key,这将为每个条目(即每个评论)分配一个单一值的数据库。
  • content: 评论的内容。 db.Text 表示列包含长文本。
  • postid: 您使用 db.ForeignKey() 类构建的整数 _foreign key,这是一种链接一个表到另一个表的密钥,使用该表的主要密钥。 此链接链接到一个评论使用邮件的主要密钥,即其ID。 此处, post 表是一个 _parent

评论模型中,特殊的 __repr__函数显示评论内容的前20个字符,以给评论对象一个短的字符串表示。

app.py文件现在将看起来如下:

 1[label flask_app/app.py]
 2import os
 3from flask import Flask, render_template, request, redirect, url_for
 4from flask_sqlalchemy import SQLAlchemy
 5
 6basedir = os.path.abspath(os.path.dirname(__file__))
 7
 8app = Flask(__name__)
 9app.config['SQLALCHEMY_DATABASE_URI'] =\
10           'sqlite:///' + os.path.join(basedir, 'database.db')
11app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
12
13db = SQLAlchemy(app)
14
15class Post(db.Model):
16    id = db.Column(db.Integer, primary_key=True)
17    title = db.Column(db.String(100))
18    content = db.Column(db.Text)
19    comments = db.relationship('Comment', backref='post')
20
21    def __repr__(self):
22        return f'<Post "{self.title}">'
23
24class Comment(db.Model):
25    id = db.Column(db.Integer, primary_key=True)
26    content = db.Column(db.Text)
27    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
28
29    def __repr__(self):
30        return f'<Comment "{self.content[:20]}...">'

保存并关闭app.py

创建数据库

现在您已经设置了数据库连接和帖子和评论模型,您将使用Flask壳创建您的数据库和帖子和评论表,基于您声明的模型。

当您的虚拟环境启用时,使用环境变量FLASK_APPapp.py文件设置为您的Flask应用程序:

1export FLASK_APP=app

然后使用以下命令在您的 flask_app 目录中打开 Flask 壳:

1flask shell

将打开一个 Python 交互式壳,该特殊壳在您的 Flask 应用程序的背景下运行命令,以便您所呼叫的 Flask-SQLAlchemy 函数连接到您的应用程序。

导入数据库对象和帖子和评论模型,然后运行db.create_all()函数来创建与模型相关的表格:

1from app import db, Post, Comment
2db.create_all()

让壳运行,打开另一个终端窗口,并导航到您的flask_app目录. 在flask_app中,您将看到一个名为database.db的新文件。

<$>[注] 注: db.create_all() 函数不会重新创建或更新一个表,如果它已经存在,例如,如果您通过添加一个新的列来修改您的模型,然后运行 db.create_all() 函数,如果该表已经存在于数据库中,您对该模型所做的更改将不会应用到表中。

1db.drop_all()
2db.create_all()

这将适用于您对模型所做的更改,但还会删除数据库中的所有现有数据. 要更新数据库结构并保存现有数据,您需要使用 schema migration,允许您修改表并保存数据。

如果您收到错误,请确保数据库 URI 和模型声明正确。

人口表

在创建数据库和帖子和评论表后,您将在您的flask_app目录中创建一个文件,将一些帖子和评论添加到您的数据库中。

打开一个名为init_db.py的新文件:

1nano init_db.py

此文件将创建三个帖子对象和四个评论对象,并将其添加到数据库中:

 1[label flask_app/init_db.py]
 2from app import db, Post, Comment
 3
 4post1 = Post(title='Post The First', content='Content for the first post')
 5post2 = Post(title='Post The Second', content='Content for the Second post')
 6post3 = Post(title='Post The Third', content='Content for the third post')
 7
 8comment1 = Comment(content='Comment for the first post', post=post1)
 9comment2 = Comment(content='Comment for the second post', post=post2)
10comment3 = Comment(content='Another comment for the second post', post_id=2)
11comment4 = Comment(content='Another comment for the first post', post_id=1)
12
13db.session.add_all([post1, post2, post3])
14db.session.add_all([comment1, comment2, comment3, comment4])
15
16db.session.commit()

保存并关闭文件。

在这里,您从app.py文件中导入数据库对象、Post模型和Comment模型。

您使用邮件模型创建几个帖子对象,将帖子的标题传递到标题参数,而帖子的内容传递到内容参数。

然后创建几个评论对象,传递评论的内容. 您可以使用两种方法将评论与它所属的帖子相关联。 您可以将帖子对象传递到帖子参数,如帖子1帖子2对象所示。 您也可以传递帖子ID到帖子ID参数,如帖子3帖子4对象所示。

定义帖子和评论对象后,您使用「db.session.add_all()」将所有帖子和评论对象添加到数据库会话中,该会话管理交易,然后使用「db.session.commit()」方法进行交易并将更改应用到数据库中。

运行init_db.py文件来执行代码并将数据添加到数据库中:

1python init_db.py

要查看您添加到数据库的数据,请打开瓶壳以查询所有帖子,并显示其标题和每个帖子的评论内容:

1flask shell

运行以下代码. 此查询所有帖子,并显示每个帖子的标题和下面每个帖子的评论:

1from app import Post
2
3posts = Post.query.all()
4
5for post in posts:
6    print(f'## {post.title}')
7    for comment in post.comments:
8            print(f'> {comment.content}')
9    print('----')

在这里,你从 app.py 文件中导入邮件模型。你使用查询属性上的all()方法来查询数据库中存在的所有邮件,然后将结果存储在一个名为邮件的变量中。然后你使用一个循环来通过邮件变量中的每个项目。你打印了标题,然后使用另一个循环来通过邮件中的每个评论。你使用post.comments访问邮件的评论。你打印了评论的内容,然后打印了字符串...来分开邮件。

你会得到以下的输出:

 1[secondary_label Output]
 2
 3## Post The First
 4> Comment for the first post
 5> Another comment for the first post
 6----
 7## Post The Second
 8> Comment for the second post
 9> Another comment for the second post
10----
11## Post The Third
12----

正如您所看到的,您可以使用很少的代码访问每个帖子的数据和每个帖子的评论。

现在离开壳:

1exit()

此时,您在数据库中有多个帖子和评论,接下来,您将为索引页面创建一个Flask路线,并在其上显示数据库中的所有帖子。

步骤 3 - 显示所有帖子

在此步骤中,您将创建路线和模板,以在索引页面上显示数据库中的所有帖子。

打开您的app.py文件,将索引页面的路线添加到它:

1nano app.py

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

1[label flask_app/app.py]
2
3# ...
4
5@app.route('/')
6def index():
7    posts = Post.query.all()
8    return render_template('index.html', posts=posts)

保存并关闭文件。

在这里,您使用app.route()装饰器创建一个index()视图函数. 在此函数中,您查询数据库并获得所有帖子,就像您在上一步一样。

在您创建index.html模板文件以显示数据库中的现有帖子之前,您将首先创建一个基准模板,该模板将具有其他模板也将使用的所有基本HTML代码,以避免代码重复。

创建一个模板目录,然后打开一个名为base.html的新模板:

1mkdir templates
2nano templates/base.html

将以下代码添加到base.html文件中:

 1[label flask_app/templates/base.html]
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="UTF-8">
 7    <title>{% block title %} {% endblock %} - FlaskApp</title>
 8    <style>
 9        .title {
10            margin: 5px;
11        }
12
13        .content {
14            margin: 5px;
15            width: 100%;
16            display: flex;
17            flex-direction: row;
18            flex-wrap: wrap;
19        }
20
21        .comment {
22            padding: 10px;
23            margin: 10px;
24            background-color: #fff;
25        }
26
27        .post {
28            flex: 20%;
29            padding: 10px;
30            margin: 5px;
31            background-color: #f3f3f3;
32            inline-size: 100%;
33        }
34
35        .title a {
36            color: #00a36f;
37            text-decoration: none;
38        }
39
40        nav a {
41            color: #d64161;
42            font-size: 3em;
43            margin-left: 50px;
44            text-decoration: none;
45        }
46
47    </style>
48</head>
49<body>
50    <nav>
51        <a href="{{ url_for('index') }}">FlaskApp</a>
52        <a href="#">Comments</a>
53        <a href="#">About</a>
54    </nav>
55    <hr>
56    <div class="content">
57        {% block content %} {% endblock %}
58    </div>
59</body>
60</html>

保存并关闭文件。

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

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

1nano templates/index.html

添加以下代码:

 1[label flask_app/templates/index.html]
 2{% extends 'base.html' %}
 3
 4{% block content %}
 5    <span class="title"><h1>{% block title %} Posts {% endblock %}</h1></span>
 6    <div class="content">
 7        {% for post in posts %}
 8            <div class="post">
 9                <p><b>#{{ post.id }}</b></p>
10                <b>
11                    <p class="title">
12                        <a href="#">
13                            {{ post.title }}
14                        </a>
15                    </p>
16                </b>
17                <div class="content">
18                    <p>{{ post.content }}</p>
19                </div>
20                <hr>
21            </div>
22        {% endfor %}
23    </div>
24{% endblock %}

保存并关闭文件。

在这里,您将基础模板扩展并取代内容块的内容。您使用一个<h1>标题,它也作为一个标题。您在{% for post in posts %}行中使用一个Jinja for loop](https://jinja.palletsprojects.com/en/3.0.x/templates/#for),通过您从index()视图函数转移到这个模板的posts变量中的每个帖子。您将显示邮件ID、其标题和邮件内容。

在您的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/

你会看到你添加到数据库的帖子在一个类似于以下的页面:

Index Page

您已经在索引页面上显示了您在数据库中的帖子,接下来,您将为帖子页面创建一条路线,在那里您将显示每个帖子的详细信息及其评论。

步骤 4 – 显示单个帖子及其评论

在此步骤中,您将创建一条路线和模板,以在专用页面上显示每个帖子的详细信息,以及该帖子的评论。

在此步骤结束时,URL http://127.0.0.1:5000/1将是一个显示第一个帖子的页面(因为它具有ID 1)及其评论。URL http://127.0.0.1:5000/ID将显示该帖子与相关的 ID号码,如果有的话。

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

打开app.py进行更改:

1nano app.py

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

1[label flask_app/app.py]
2# ...
3
4@app.route('/<int:post_id>/')
5def post(post_id):
6    post = Post.query.get_or_404(post_id)
7    return render_template('post.html', post=post)

保存并关闭文件。

在这里,您使用路线 '/<int:post_id>/'',而 int:` 是一个 转换器将 URL 中的默认字符串转换为整数。

该 ID 通过post_id参数从 URL 传输到post()视图函数。在该函数内,您可以查询邮件表并使用get_or_404()方法获取邮件的 ID。这将将邮件数据保存到post变量中,如果存在的话,并以404 Not Found HTTP 错误响应,如果数据库中没有带有给定的 ID 的邮件。

您渲染了一个名为post.html的模板,并将其传递给您获取的帖子。

打开这个新的post.html模板文件:

1nano templates/post.html

这将类似于index.html模板,但它只会显示单个帖子:

 1[label flask_app/templates/post.html]
 2{% extends 'base.html' %}
 3
 4{% block content %}
 5    <span class="title"><h1>{% block title %} {{ post.title }}  {% endblock %}</h1></span>
 6    <div class="content">
 7            <div class="post">
 8                <p><b>#{{ post.id }}</b></p>
 9                <b>
10                    <p class="title">{{ post.title }}</p>
11                </b>
12                <div class="content">
13                    <p>{{ post.content }}</p>
14                </div>
15                <hr>
16                <h3>Comments</h3>
17                {% for comment in post.comments %}
18                    <div class="comment">
19                        <p>#{{ comment.id }}</p>
20                        <p>{{ comment.content }}</p>
21                    </div>
22                {% endfor %}
23            </div>
24    </div>
25{% endblock %}

保存并关闭文件。

在这里,您可以扩展基本模板,将帖子标题设置为页面标题,显示帖子ID、帖子标题和帖子内容。

使用您的浏览器导航到第二篇文章的URL:

1http://127.0.0.1:5000/2/

你会看到一个类似于以下的页面:

Single Post Page

接下来,编辑index.html,使帖子的标题链接到个别帖子:

1nano templates/index.html

for循环中编辑帖子标题链接的href属性值:

 1[label flask_app/templates/index.html]
 2
 3...
 4
 5{% for post in posts %}
 6    <div class="post">
 7        <p><b>#{{ post.id }}</b></p>
 8        <b>
 9            <p class="title">
10                <a href="{{ url_for('post', post_id=post.id)}}">
11                {{ post.title }}
12                </a>
13            </p>
14        </b>
15        <div class="content">
16            <p>{{ post.content }}</p>
17        </div>
18        <hr>
19    </div>
20{% endfor %}

保存并关闭文件。

导航到您的索引页面或更新它:

1http://127.0.0.1:5000/

点击索引页面的每个帖子标题,现在你会看到每个帖子链接到正确的帖子页面。

您现在已经创建了一个页面来显示单个帖子,接下来,您将添加一个网页表格到帖子页面,以允许用户添加新评论。

步骤 5 – 添加新评论

在此步骤中,您将编辑 /<int:post_id>/ 路径及其 post() 视图功能,该功能处理显示单个帖子. 您将在每个帖子下方添加网页表格,以允许用户添加评论到该帖子,然后您将处理评论提交并添加到数据库中。

首先,打开post.html模板文件,添加一个包含评论内容的文本区域的网页表格,以及一个添加评论**提交按钮。

1nano templates/post.html

通过在评论H3标题下方添加表单,并直接在循环上方编辑文件:

 1[label flask_app/templates/post.html]
 2
 3<hr>
 4<h3>Comments</h3>
 5<form method="post">
 6    <p>
 7        <textarea name="content"
 8                    placeholder="Comment"
 9                    cols="60"
10                    rows="5"></textarea>
11    </p>
12
13    <p>
14        <button type="submit">Add comment</button>
15    </p>
16</form>
17{% for comment in post.comments %}

保存并关闭文件。

在这里,您添加一个<form>标签,其属性方法设置为发布,表示该表单将提交 POST 请求。

您有评论内容的文本区域和提交按钮。

随着开发服务器运行,使用您的浏览器导航到一个帖子:

1http://127.0.0.1:5000/2/

你会看到一个类似于以下的页面:

Single Post Page with Comment Form

此表单将向post()视图函数发送 POST 请求,但由于没有处理表单提交的代码,表单目前无法工作。

接下来,您将添加代码到post()视图函数来处理表单提交,并将新评论添加到数据库中。

1nano app.py

编辑 /<int:post_id>/ 路线和其 post() 视图函数以显示如下:

 1[label flask_app/app.py]
 2
 3@app.route('/<int:post_id>/', methods=('GET', 'POST'))
 4def post(post_id):
 5    post = Post.query.get_or_404(post_id)
 6    if request.method == 'POST':
 7        comment = Comment(content=request.form['content'], post=post)
 8        db.session.add(comment)
 9        db.session.commit()
10        return redirect(url_for('post', post_id=post.id))
11
12    return render_template('post.html', post=post)

保存并关闭文件。

您可以使用方法参数允许 GET 和 POST 请求。GET 请求用于从服务器中获取数据。POST 请求用于将数据发送到特定路径。

您使用评论模型创建评论对象,将您从请求。form对象中提取的提交评论内容传递给它.您使用post参数指定评论属于的帖子,通过get_or_404()方法传递给您使用邮件ID获取的post对象。

您可以将您构建的评论对象添加到数据库会话中,进行交易,并重定向到帖子页面。

现在刷新你的浏览器上的帖子页面,写评论,并提交它,你会看到你的新评论在帖子下面。

有关 Web 表单的更多信息,请参阅 如何在 Flask 应用程序中使用 Web 表单。 有关管理 Web 表单的更先进和更安全的方法,请参阅 如何使用和验证 Web 表单使用 Flask-WTF

步骤 6 - 显示所有评论

在此步骤中,您将添加一个 评论页面,在那里您将显示数据库中的所有评论,通过显示最新的评论来排序它们。

打开app.py:

1nano app.py

将下列路径添加到文件的末尾. 这将收集数据库中的所有评论,排序为最新的首先. 然后将其传输到名为 comments.html 的模板文件中,您将后来创建:

1[label flask_app/app.py]
2# ...
3
4@app.route('/comments/')
5def comments():
6    comments = Comment.query.order_by(Comment.id.desc()).all()
7    return render_template('comments.html', comments=comments)

保存并关闭文件。

您在查询属性上使用order_by()方法以以特定顺序收集所有评论. 在这种情况下,您使用Comment.id列上的desc()方法以下行顺序收集评论,最新的评论首先出现。

您将一个名为comments.html的模板渲染,将其传递给包含由最新的最先排序的所有评论的评论对象。

打开这个新的comments.html模板文件:

1nano templates/comments.html

在里面输入以下代码. 这将显示评论,并链接到他们所属的帖子:

 1[label flask_app/templates/comments.html]
 2{% extends 'base.html' %}
 3
 4{% block content %}
 5    <span class="title"><h1>{% block title %} Latest Comments {% endblock %}</h1></span>
 6    <div class="content">
 7                {% for comment in comments %}
 8                    <div class="comment">
 9                        <i>
10                            (#{{ comment.id }})
11                            <p>{{ comment.content }}</p>
12                        </i>
13                        <p class="title">
14                        On <a href="{{ url_for('post',
15                                                post_id=comment.post.id) }}">
16                                {{ comment.post.title }}
17                              </a>
18                        </p>
19                    </div>
20                {% endfor %}
21            </div>
22    </div>
23{% endblock %}

保存并关闭文件。

在这里,你可以扩展基础模板,设置一个标题,并通过评论使用一个for循环。你会显示评论的ID,其内容,并链接到它所属的帖子。

使用您的浏览器导航到评论页面:

1http://127.0.0.1:5000/comments/

你会看到一个类似于以下的页面:

Comments Page

现在,编辑base.html模板,使 评论导航栏链接指向此评论页面:

1nano templates/base.html

编辑导航栏以显示如下:

1[label flask_app/templates/base.html]
2    <nav>
3        <a href="{{ url_for('index') }}">FlaskApp</a>
4        <a href="{{ url_for('comments') }}">Comments</a>
5        <a href="#">About</a>
6    </nav>

保存并关闭文件。

更新您的评论页面,你会看到 **评论 ** 导航栏链接工作。

您现在有一个页面,显示了数据库中的所有评论,接下来,您将在帖子页面上的每个评论下面添加一个按钮,以允许用户删除它。

第7步:删除评论

在此步骤中,您将在每个评论下面添加一个删除评论按钮,以允许用户删除不需要的评论。

首先,您将添加一个新的/comments/ID/delete路线,它接受POST请求. 视图函数将收到您想要删除的评论的ID,从数据库中获取它,删除它,并重定向到删除的评论的帖子页面。

打开app.py:

1nano app.py

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

 1[label flask_app/app.py]
 2# ...
 3
 4@app.post('/comments/<int:comment_id>/delete')
 5def delete_comment(comment_id):
 6    comment = Comment.query.get_or_404(comment_id)
 7    post_id = comment.post.id
 8    db.session.delete(comment)
 9    db.session.commit()
10    return redirect(url_for('post', post_id=post_id))

保存并关闭文件。

在这里,您使用了 Flask 版本 2.0.0中引入的 app.post]装饰器,而不是使用常见的 app.route 装饰器。例如, @app.post("/login")@app.route("/login", methods=["POST"]) 的缩略。这意味着此视图功能只接受 POST 请求,而浏览器上的 /comments/ID/delete 路线将返回一个 405 方法不允许 错误,因为 Web 浏览器默认为 GET 请求。

删除_评论()视图函数通过评论_id URL 变量获取要删除的评论的 ID. 您使用get_or_404()方法获取评论并将其保存为评论变量,或者在评论不存在的情况下用404 Not Found 响应。

您在数据库会话中使用删除()方法在db.session.delete(comment)字段中,将其传递为评论对象. 此设置会话以在交易发生时删除评论。 由于您不需要进行任何其他修改,您直接使用db.session.commit()来执行交易。 最后,您将用户重定向到已删除的评论发布的帖子。

接下来,编辑post.html模板,在每个评论下面添加一个删除评论按钮:

1nano templates/post.html

通过直接在评论内容下方添加一个新的<form>标签来编辑for循环:

 1[label flask_app/templates/post.html]
 2    {% for comment in post.comments %}
 3        <div class="comment">
 4            <p>#{{ comment.id }}</p>
 5            <p>{{ comment.content }}</p>
 6            <form method="POST"
 7                action="{{ url_for('delete_comment',
 8                                    comment_id=comment.id) }}">
 9                <input type="submit" value="Delete Comment"
10                    onclick="return confirm('Are you sure you want to delete this entry?')">
11            </form>
12        </div>
13    {% endfor %}

保存并关闭文件。

在这里,您有一个网页表格,将 POST 请求提交到 delete_comment() 视图函数中。 您将 comment.id 作为 comment_id 参数的参数,以指定将被删除的评论。 您使用在 Web 浏览器中可用的 confirm() 方法 函数,在提交请求之前显示确认消息。

现在导航到您的浏览器上的邮件页面:

1http://127.0.0.1:5000/2/

你会看到一个 删除评论按钮在每个评论下面. 点击它,并确认删除。

您现在有一个方法可以从数据库中删除评论。

结论

你建立了一个小型博客系统,展示了如何使用Flask-SQLAlchemy扩展来管理一对多关系,你学会了如何将父母表与儿童表连接起来,将儿童对象与父母联系起来,并将其添加到数据库中,以及如何从父母入口访问儿童数据,反之亦然。

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

Published At
Categories with 技术
comments powered by Disqus