作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。
介绍
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_app
和How 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/
你会看到一个类似于以下的页面:
如果您收到错误,请确保您正确遵循上述步骤。
要停止开发服务器,请使用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.py
Python程序以通过添加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/
你会看到一个类似于以下的页面:
让开发服务器运行,并打开一个新的终端窗口。
您需要在两个页面上显示每个帖子的标签:在索引页面上的每个帖子下,在帖子页面上的帖子内容下。 您将使用相同的代码来显示标签。 为了避免代码重复,您将使用一个 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
对象,这将显示每个帖子下面的标签名称。
刷新浏览器中的索引页面,你会看到每个帖子下面的标签,如下图像所示:
接下来,你会在帖子页面上的帖子内容下添加标签. 打开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/
正如你所看到的,所有被标记为写作
的帖子都显示:
现在,编辑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 应用程序系列的其他教程。