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

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

介绍

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

多对多数据库关系是两个数据库表之间的关系,每个表中的记录可以引用其他表中的多个记录,例如,在博客中,帖子的表可以与存储作者的表有多对多关系,每个帖子可以有许多作者,每个作者可以写很多帖子,因此,帖子和作者之间存在多对多关系,例如,在社交媒体应用程序中,每个帖子可能有许多标签,每个标签可能有许多帖子。

在本教程中,您将通过添加多对多关系来修改使用Flask和Flask-SQLAlchemy构建的应用程序,您将有帖子和标签之间的关系,每个博客帖子可以有多个标签,每个标签可以有多个帖子标签。

虽然您可以独立遵循本教程,但它也是如何使用Flask-SQLAlchemy的一对多数据库关系(How to Use One-to-Many Database Relationships with Flask-SQLAlchemy)(https://andsky.com/tech/tutorials/how-to-use-one-to-many-database-relationships-with-flask-sqlalchemy)教程的延续,您可以在博客应用程序中构建一个多表数据库,在帖子和评论之间有一个对许多关系。

到教程结束时,您的应用程序将有新功能来添加标签到帖子. 帖子可以用多个标签标签,每个标签页面将显示所有标签的帖子。

前提条件

在本教程中,我们将调用我们的项目目录flask_appHow To Create Your First Web Application Using Flask and Python以及How To Use Templates in a Flask Application等基本的Flask概念,如路线、视图功能和模板。如果您不熟悉Flask,请查看我们的How To Build a Website with HTML(LINK3)教程系列以获取背景知识。

步骤 1 - 设置 Web 应用程序

在此步骤中,您将设置博客应用程序,以便为修改做好准备。您还将审查Flask-SQLAlchemy数据库模型和Flask路径,以了解应用程序的结构。

为了展示Flask-SQLAlchemy对Flask Web应用程序的多对多关系的添加,您将使用上一节教程的应用程序代码,这是一个博客系统,可以添加和显示帖子,评论帖子,阅读和删除现有评论。

克隆存储库,并用以下命令将其从flask-slqa-bloggy更名为flask_app:

1git clone https://github.com/do-community/flask-slqa-bloggy flask_app

点击flask_app导航:

1cd flask_app

创建一个新的虚拟环境:

1python -m venv env

激活环境:

1source env/bin/activate

安装 Flask 和 Flask-SQLAlchemy:

1pip install Flask Flask-SQLAlchemy

然后,设置以下环境变量:

1export FLASK_APP=app
2export FLASK_ENV=development

「FLASK_APP」表示您目前正在开发的应用程序,在这种情况下是「app.py」。「FLASK_ENV」指定了该模式。

接下来,打开 Flask 壳来创建数据库表:

1flask shell

然后导入db Flask-SQLAlchemy 数据库对象、Post 模型和Comment 模型,并使用db.create_all()函数创建数据库表:

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

然后使用init_db.py程序填充数据库:

1python init_db.py

这将添加三个帖子和四个评论到数据库中。

运行开发服务器:

1flask run

如果你去你的浏览器,你将有应用程序运行在以下URL:

1http://127.0.0.1:5000/

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

Flask App Index

如果您收到错误,请确保您正确遵循上述步骤。

要停止开发服务器,请使用CTRL + C

接下来,您将通过 Flask-SQLAlchemy 数据库模型来了解表之间的当前关系. 如果您熟悉 app.py 文件的内容,您可以跳过下一步。

打开app.py文件:

1nano 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]}...">'
31
32@app.route('/')
33def index():
34    posts = Post.query.all()
35    return render_template('index.html', posts=posts)
36
37@app.route('/<int:post_id>/', methods=('GET', 'POST'))
38def post(post_id):
39    post = Post.query.get_or_404(post_id)
40    if request.method == 'POST':
41        comment = Comment(content=request.form['content'], post=post)
42        db.session.add(comment)
43        db.session.commit()
44        return redirect(url_for('post', post_id=post.id))
45
46    return render_template('post.html', post=post)
47
48@app.route('/comments/')
49def comments():
50    comments = Comment.query.order_by(Comment.id.desc()).all()
51    return render_template('comments.html', comments=comments)
52
53@app.post('/comments/<int:comment_id>/delete')
54def delete_comment(comment_id):
55    comment = Comment.query.get_or_404(comment_id)
56    post_id = comment.post.id
57    db.session.delete(comment)
58    db.session.commit()
59    return redirect(url_for('post', post_id=post_id))

在这里,您有两个数据库模型,代表两个表:

  • 帖子:具有标识列、标题、内容以及与评论表的一对多关系。
  • 评论:具有标识列、内容列和post_id列来引用评论的帖子。

在模型下方,您有以下路线:

  • /: 索引页面,显示数据库中的所有帖子。
  • /comments/: 单个帖子页面. 例如,链接 http://127.0.0.1:5000/2/显示数据库中的第二篇文章及其评论的详细信息。
  • /comments/: 显示数据库中的所有评论和链接到每个评论的帖子。

关闭app.py文件。

在下一步中,您将使用多对多关系创建两个表之间的链接。

步骤 2 — 设置数据库模型为多对多关系

在此步骤中,您将添加一个数据库模型,该模型将代表标签表。您将它链接到现有的帖子表,使用 association table,这是一个将您的两个表连接到一个多对多关系的表。 一个多对多关系链接两个表,其中一个表中的每个项目在另一个表中有许多相关项目。 在关联表中,每个帖子将参考其标签,每个标签将参考与其标记的帖子。

假设你有一个简单的博客帖子的表,如下:

1Posts
2+----+-----------------------------------+
3| id | content                           |
4+----+-----------------------------------+
5| 1  | A post on life and death          |
6| 2  | A post on joy                     |
7+----+-----------------------------------+

还有这样一个标签的桌子:

1Tags
2+----+-------+
3| id | name  |
4+----+-------+
5| 1  | life  |
6| 2  | death |
7| 3  | joy   |
8+----+-------+

假设你想用生命死亡标签标签关于生命和死亡的帖子

1Posts
2+----+-----------------------------------+------+
3| id | content                           | tags |
4+----+-----------------------------------+------+
5| 1  | A post on life and death          | 1, 2 |
6| 2  | A post on joy                     |      |
7+----+------------------------------------------+

这种方法不起作用,因为每个列应该只有一个值。如果您有多个值,则数据添加和更新等基本操作变得繁琐和缓慢。相反,应该有一个第三个表引用相关表的主要密钥 - 这个表通常被称为 _association 表或 _join 表,并存储每个表中的每个项目的 ID。

以下是链接帖子和标签之间的关联表的示例:

1post_tag
2+----+---------+-------------+
3| id | post_id | tag_id      |
4+----+---------+-------------+
5| 1  | 1       | 1           |
6| 2  | 1       | 2           |
7+----+---------+-------------+

在第一行中,具有ID 1’的帖子(即关于生命和死亡的帖子)与具有ID 1’(‘生命’)的标签有关。在第二行中,同一帖子也与具有ID `2’(‘死亡’)的标签有关。

现在,您将修改app.py文件,添加一个新的数据库模型,代表您将使用的标签存储表。

首先,打开app.py来建立帖子和标签之间的关系:

1nano app.py

db对象和Post模型上方添加一个post_tag表和一个Tag模型,然后在Post模型中添加一个tags关系假列,这样您可以通过post.tags访问帖子的标签,并通过tag.posts访问标签的帖子:

 1[label flask_app/app.py]
 2
 3# ...
 4
 5db = SQLAlchemy(app)
 6
 7post_tag = db.Table('post_tag',
 8                    db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
 9                    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
10                    )
11
12class Tag(db.Model):
13    id = db.Column(db.Integer, primary_key=True)
14    name = db.Column(db.String(50))
15
16    def __repr__(self):
17        return f'<Tag "{self.name}">' 
18
19class Post(db.Model):
20    id = db.Column(db.Integer, primary_key=True)
21    title = db.Column(db.String(100))
22    content = db.Column(db.Text)
23    comments = db.relationship('Comment', backref='post')
24    tags = db.relationship('Tag', secondary=post_tag, backref='posts')
25
26    def __repr__(self):
27        return f'<Post "{self.title}">'

保存并关闭文件。

在这里,您可以使用 db.Table() 函数创建具有两个列的表. 对于关联表,最好使用表而不是数据库模型。

post_tag 表有两个列,代表两个 foreign key,这些键用于引用另一个表中的主要密钥列:

  • post_id:代表邮件 ID 并引用 post 表中的 ID 列的整数 foreign key.
  • tag_id:代表标签 ID 并引用 tag 表中的 ID 列的整数 foreign key.

这些键建立了表之间的关系。

post_tag表下方,你创建一个Tag模型,它代表你将存储标签的表。

  • id:标签的ID.
  • name:标签的名称.

在特殊的 `repr() 方法中,您可以使用标签的名称来为每个标签对象提供清晰的字符串表示,用于调试。

您将一个标签类变量添加到邮件模型中,您使用db.relationship()方法,将标签模型的名称传递给它(在这种情况下,标签)。

您将post_tag关联表转移到二级参数,以建立帖子和标签之间的多对多关系。

您可以使用backref参数将作为一个列的回归引用添加到标签模型中。这样,您可以通过tag.posts访问标签的帖子,并通过post.tags访问帖子的标签。

接下来,编辑init_db.pyPython程序以通过添加post_tag关联表和基于Tag模型的标签表来修改数据库:

1nano init_db.py

编辑文件以显示如下:

 1[label flask_app/init_db.py]
 2from app import db, Post, Comment, Tag
 3
 4db.drop_all()
 5db.create_all()
 6
 7post1 = Post(title='Post The First', content='Content for the first post')
 8post2 = Post(title='Post The Second', content='Content for the Second post')
 9post3 = Post(title='Post The Third', content='Content for the third post')
10
11comment1 = Comment(content='Comment for the first post', post=post1)
12comment2 = Comment(content='Comment for the second post', post=post2)
13comment3 = Comment(content='Another comment for the second post', post_id=2)
14comment4 = Comment(content='Another comment for the first post', post_id=1)
15
16tag1 = Tag(name='animals')
17tag2 = Tag(name='tech')
18tag3 = Tag(name='cooking')
19tag4 = Tag(name='writing')
20
21post1.tags.append(tag1)  # Tag the first post with 'animals'
22post1.tags.append(tag4)  # Tag the first post with 'writing'
23post3.tags.append(tag3)  # Tag the third post with 'cooking'
24post3.tags.append(tag2)  # Tag the third post with 'tech'
25post3.tags.append(tag4)  # Tag the third post with 'writing'
26
27db.session.add_all([post1, post2, post3])
28db.session.add_all([comment1, comment2, comment3, comment4])
29db.session.add_all([tag1, tag2, tag3, tag4])
30
31db.session.commit()

保存并关闭文件。

在这里,您导入标签模型. 您使用db.drop_all()函数删除数据库中的所有内容,以安全地添加标签和post_tag表格,并避免添加新表格到数据库中的常见问题。

在之前的教程声明帖子和评论的代码后,您使用标签模型创建四个标签。

然后,您使用在 app.py 文件中添加的 tags = db.relationship('Tag', secondary=post_tag, backref='posts') 行中添加的 tags` 属性添加到帖子中。

接下来,您可以使用db.session.add_all()函数添加创建的标签到数据库会话。

<$>[注] 注:

例如,如果您通过添加一个新列并运行db.create_all()函数来修改您的模型,则您对该模型所做的更改将不会应用到表中,如果该表已经存在于数据库中。

要更新数据库并保存现有数据,您需要使用 schema migration,允许您修改表并保存数据。您可以使用 Flask-Migrate扩展来通过 Flask 命令行接口执行 SQLAlchemy 方案迁移。

运行init_db.py程序,将更改应用到数据库:

1python init_db.py

如果您看到错误,请确保您对 init_db.py 文件进行了正确的更改。

要查看当前在数据库中的帖子和标签,请打开 Flask 壳:

1flask shell

运行以下Python代码,通过帖子和标签循环:

1from app import Post
2
3posts = Post.query.all()
4
5for post in posts:
6    print(post.title)
7    print(post.tags)
8    print('---')

在这里,你从app.py导入Post模型. 你查询了帖子表,并收集了数据库中的所有帖子. 你循环通过帖子,并打印了与每个帖子相关的帖子标题和标签列表。

您将获得类似于以下的输出:

 1[secondary_label Output]
 2
 3Post The First
 4[<Tag "animals">, <Tag "writing">]
 5---
 6Post The Third
 7[<Tag "cooking">, <Tag "tech">, <Tag "writing">]
 8---
 9Post The Second
10[]
11---

您可以使用tag.name访问标签名称,如下示例所示,您可以使用 Flask 壳运行:

 1from app import Post
 2
 3posts = Post.query.all()
 4
 5for post in posts:
 6    print('TITLE: ', post.title)
 7    print('-')
 8    print('TAGS:')
 9    for tag in post.tags:
10        print('> ', tag.name)
11    print('-'*30)

在这里,除了打印帖子的标题外,您还可以通过每个帖子的标签来打印标签名称。

您将获得类似于以下的输出:

 1[secondary_label Output]
 2TITLE:  Post The First
 3-
 4TAGS:
 5>  animals
 6>  writing
 7------------------------------
 8TITLE:  Post The Third
 9-
10TAGS:
11>  cooking
12>  tech
13>  writing
14------------------------------
15TITLE:  Post The Second
16-
17TAGS:
18------------------------------

正如你所看到的,你在init_db.py程序中添加到帖子的标签与他们被标记的帖子相关联。

若要通过tag.posts查看如何访问用特定标签标记的帖子,请在 Flask 壳中运行以下代码:

 1from app import Tag
 2
 3writing_tag = Tag.query.filter_by(name='writing').first()
 4
 5for post in writing_tag.posts:
 6    print(post.title)
 7    print('-'*6)
 8    print(post.content)
 9    print('-')
10    print([tag.name for tag in post.tags])
11    print('-'*20)

你导入了标签模型,然后在查询属性上使用filter_by()方法,通过一个名称参数来获取写字标签的名称,然后使用first()方法获得第一个结果。你将标签对象存储在名为writing_tag的变量中。有关filter_by方法的更多信息,请参阅《如何在Flask 应用程序中使用 Flask-SQLAlchemy 与数据库进行交互》(https://andsky.com/tech/tutorials/how-to-use-flask-sqlalchemy-to-interact-with-databases-in-a-flask-application#step-4-displaying-a-single-record)教程。

您通过写字标签的帖子进行循环,您可以通过写字_tag.posts访问这些帖子,您可以使用基于帖子的标签构建的帖子标题、内容和标签名单,您可以通过post.tags访问该帖子的标签。

您将获得类似于以下的输出:

 1[secondary_label Output]
 2Post The Third
 3------
 4Content for the third post
 5-
 6['cooking', 'tech', 'writing']
 7--------------------
 8Post The First
 9------
10Content for the first post
11-
12['animals', 'writing']
13--------------------

在这里,你可以看到两个被标记为写作的帖子,并在Python列表中显示标签名称。

您现在可以访问帖子及其标签,并访问特定标签的帖子。

您已添加了代表标签表的数据库模型. 您使用关联表链接了帖子和标签,并在数据库中插入了几个标签,并与它们标记了帖子. 您访问了帖子及其标签以及单个标签的帖子。

步骤 3 – 在多对多关系中管理数据

在此步骤中,您将使用 Flask 壳添加新的帖子到数据库,添加标签,并在帖子和标签之间链接。

首先,如果您的编程环境已启用,如果您尚未打开Flask壳:

1flask shell

接下来,添加几个帖子和标签:

 1from app import db, Post, Tag
 2
 3life_death_post = Post(title='A post on life and death', content='life and death')
 4joy_post = Post(title='A post on joy', content='joy')
 5
 6life_tag = Tag(name='life')
 7death_tag = Tag(name='death')
 8joy_tag = Tag(name='joy')
 9
10life_death_post.tags.append(life_tag)
11life_death_post.tags.append(death_tag)
12joy_post.tags.append(joy_tag)
13
14db.session.add_all([life_death_post, joy_post, life_tag, death_tag, joy_tag])
15
16db.session.commit()

这会创建两个帖子和三个标签,您将帖子标记为相关标签,然后使用add_all()方法将新创建的项目添加到数据库会话中,然后使用commit()方法对数据库进行更改,并将其应用到数据库中。

接下来,使用Flask壳来获取所有帖子及其标签,就像您在上一个步骤中所做的:

1posts = Post.query.all()
2
3for post in posts:
4    print(post.title)
5    print(post.tags)
6    print('---')

您将获得类似于以下的输出:

 1[secondary_label Output]
 2
 3Post The First
 4[<Tag "animals">, <Tag "writing">]
 5---
 6Post The Third
 7[<Tag "cooking">, <Tag "tech">, <Tag "writing">]
 8---
 9Post The Second
10[]
11---
12A post on life and death
13[<Tag "life">, <Tag "death">]
14---
15A post on joy
16[<Tag "joy">]
17---

您可以看到这些帖子加上他们的标签。

要展示如何在多到多数据库关系中打破两个项目之间的关系,让我们假设发布第三的帖子不再是关于烹饪的,所以你必须从中删除烹饪标签。

首先,获取您想要删除的帖子和标签:

1from app import db, Post, Tag
2post = Post.query.filter_by(title='Post The Third').first()
3tag = Tag.query.filter_by(name='cooking').first()
4print(post.title)
5print(post.tags)
6print(tag.posts)

在这里,您可以使用filter_by()方法获取标题为邮件的帖子,您可以打印邮件的标题,其标签,以及标签为邮件的帖子。

filter_by() 方法返回一个查询对象,您可以使用all() 方法获取所有结果的列表,但由于我们希望在这种情况下只有一个结果,您可以使用first() 方法获取第一个(和唯一的)结果。

你会得到以下的输出:

1[secondary_label Output]
2
3Post The Third
4
5[<Tag "cooking">, <Tag "tech">, <Tag "writing">]
6
7[<Post "Post The Third">]

在这里,您可以看到帖子标题,帖子标签,以及标记烹饪标签的帖子列表。

要从帖子中删除cooking标签,请使用remove()方法:

1post.tags.remove(tag)
2db.session.commit()
3print(post.tags)
4print(tag.posts)

在这里,您使用删除()方法将cooking标签从帖子中分离,然后使用db.session.commit()方法将更改应用到数据库中。

您将收到一个输出,确认标签已从帖子中删除:

1[secondary_label Output]
2[<Tag "tech">, <Tag "writing">]
3
4[]

正如你所看到的,烹饪标签已不在post.tags列表中,该帖子已从tag.posts列表中删除。

离开瓶子壳:

1exit()

你已经添加了新的帖子和标签,你标记了帖子,你从帖子中删除了标签,接下来,你将显示每个帖子的标签在Flask网站博客的索引页面。

步骤 4 – 显示每个帖子下方的标签

在此步骤中,您将编辑索引模板以在每个帖子下显示标签。

首先,看看Flask网站的当前主页。

当您的编程环境启用时,请使用FLASK_APP环境变量告诉Flask关于应用程序(在这种情况下是app.py)然后将FLASK_ENV环境变量设置为开发,以便在开发模式下运行应用程序:

1export FLASK_APP=app
2export FLASK_ENV=development

接下来,运行应用程序:

1flask run

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

1http://127.0.0.1:5000/

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

Index Page

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

您需要在两个页面上显示每个帖子的标签:在索引页面上的每个帖子下,在帖子页面上的帖子内容下。 您将使用相同的代码来显示标签。 为了避免代码重复,您将使用一个 Jinja 宏,该宏行为像一个 Python 函数。

首先,在您的模板目录中打开一个名为macros.html的新文件:

1nano templates/macros.html

添加以下代码:

 1[label flask_app/templates/macros.html]
 2{% macro display_tags(post) %}
 3    <div class="tags">
 4        <p>
 5            <h4>Tags:</h4>
 6            {% for tag in post.tags %}
 7                <a href="#" style="text-decoration: none; color: #dd5b5b">
 8                    {{ tag.name }}
 9                </a>
10                |
11            {% endfor %}
12        </p>
13    </div>
14{% endmacro %}

保存并关闭文件。

在这里,你使用macro关键字来声明一个名为display_tags()的宏与一个名为post的参数。你使用一个<div>标签,其中你显示一个<h4>标题。你使用一个for循环来通过邮件对象的标签,这将作为一个参数传递给宏,当你调用它时,类似于一个参数在Python函数调用中传递的方式。你通过post.tags获取标签。你将显示标签名称在一个<a>标签中。你将后来编辑href属性的值,以链接到你将创建的标签页,其中显示了所有标记特定标签的帖子。你使用macroendroyword来指定宏

接下来,要在索引页面上的每个帖子下显示标签,请打开index.html模板文件:

1nano templates/index.html

首先,您需要从 macros.html 文件中导入 display_tags() 宏,在 `{% extends 'base.html' %}' 行上方添加以下导入:

1[label flask_app/templates/index.html]
2{% from 'macros.html' import display_tags %}
3{% extends 'base.html' %}

接下来,通过以下方式调用display_tags()宏来编辑for post in posts循环:

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

保存并关闭文件。

您将称呼display_tags()宏,并将其传递给post对象,这将显示每个帖子下面的标签名称。

刷新浏览器中的索引页面,你会看到每个帖子下面的标签,如下图像所示:

Index Page with Tags

接下来,你会在帖子页面上的帖子内容下添加标签. 打开post.html模板文件:

1nano templates/post.html

首先,导入顶部的display_tags宏:

1[label flask_app/templates/post.html]
2{% from 'macros.html' import display_tags %}
3{% extends 'base.html' %}

然后调用display_tags()宏,将其传递到帖子对象下方的帖子内容和<hr>标签上方:

 1[label flask_app/templates/post.html]
 2<div class="post">
 3    <p><b>#{{ post.id }}</b></p>
 4    <b>
 5        <p class="title">{{ post.title }}</p>
 6    </b>
 7    <div class="content">
 8        <p>{{ post.content }}</p>
 9    </div>
10
11    {{ display_tags(post) }}
12
13    <hr>
14    <h3>Comments</h3>

保存并关闭文件。

现在,导航到一个帖子页面:

1http://127.0.0.1:5000/2

您将看到标签以与索引页面上显示的标签相同的方式显示。

您已经显示了您在每个帖子中添加的标签,接下来,您将为您的 Flask 应用程序添加一个新的路线,该路线将显示具有特定标签的所有帖子,然后您将使您在此步骤中显示的标签链接功能。

步骤 5 – 显示标签和他们的帖子

在此步骤中,您将向 Web 应用程序添加路线和模板,以显示您在数据库中的标签及其帖子。

例如,路线 /tags/tag_name/ 会显示显示所有标记为 tag_name 的帖子的一页。

打开app.py来编辑:

1nano app.py

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

1[label flask_app/app.py]
2
3# ...
4
5@app.route('/tags/<tag_name>/')
6def tag(tag_name):
7    tag = Tag.query.filter_by(name=tag_name).first_or_404()
8    return render_template('tag.html', tag=tag)

保存并关闭文件。

在这里,您使用一个名为tag_name的 URL 变量来确定标签和标记的帖子将在标签页面上显示,标签名将通过tag_name参数传递到tag()视图函数,您使用filter_by()方法查询标签,您使用first_or_404()来获取标签对象并将其存储在名为tag的变量中,或者以404 Not Found错误消息回复,如果数据库中没有给出的名称标签。

然后,你将一个名为tag.html的模板文件转换为tag对象。

打开新的templates/tag.html来编辑:

1nano templates/tag.html

添加以下代码:

 1[label flask_app/templates/tag.html]
 2{% from 'macros.html' import display_tags %}
 3{% extends 'base.html' %}
 4
 5{% block content %}
 6    <span class="title">
 7        <h1>{% block title %} Posts Tagged with "{{ tag.name }}" {% endblock %}</h1>
 8    </span>
 9    <div class="content">
10        {% for post in tag.posts %}
11        <div class="post">
12            <p><b>#{{ post.id }}</b></p>
13            <b>
14                <p class="title">
15                    <a href="{{ url_for('post', post_id=post.id)}}">
16                        {{ post.title }}
17                    </a>
18                </p>
19            </b>
20            <div class="content">
21                <p>{{ post.content }}</p>
22            </div>
23
24            {{ display_tags(post) }}
25
26            <hr>
27        </div>
28        {% endfor %}
29    </div>
30{% endblock %}

保存并关闭文件。

您从 macros.html 中导入display_tags()宏,然后扩展基板模板。

在内容块中,您将标题设置为包含标签名称的标题,然后循环通过标记的帖子,您可以通过tag.posts访问这些帖子。

随着开发服务器的运行,导航到以下URL:

1http://127.0.0.1:5000/tags/writing/

正如你所看到的,所有被标记为写作的帖子都显示:

Tag Page

现在,编辑display_tags()宏,使标签链接功能。

1nano templates/macros.html

编辑href属性的值如下:

 1[label flask_app/templates/macros.html]
 2
 3{% macro display_tags(post) %}
 4    <div class="tags">
 5        <p>
 6            <h4>Tags:</h4>
 7            {% for tag in post.tags %}
 8            <a href="{{ url_for('tag', tag_name=tag.name) }}"
 9               style="text-decoration: none; color: #dd5b5b">
10                    {{ tag.name }}
11                </a>
12                |
13            {% endfor %}
14        </p>
15    </div>
16{% endmacro %}

保存并关闭文件。

刷新已使用「display_tags()」宏的页面,您将看到标签链接现在功能:

1http://127.0.0.1:5000/
2http://127.0.0.1:5000/2/
3http://127.0.0.1:5000/tags/writing/

正如您所看到的,使用 Jinja 宏允许您重复使用代码,并且编辑一个宏会应用多个模板上的更改。

您已添加了单个标签页面,用户可以查看具有特定标签的所有帖子,而帖子下的标签现在链接到这个新页面。

结论

您在博客系统中添加的标签功能展示了如何使用 Flask-SQLAlchemy 扩展来管理多对多关系,您学会了如何使用关联表(也称为 join table)链接两个相关表之间,将一个条目与另一个条目关联,将条目添加到数据库,并从条目中访问和分离数据。

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

Published At
Categories with 技术
comments powered by Disqus