[英]SQLAlchemy query returning count of ALL join rows, not grouping by joined row
I'm building a CRUD application and trying to display a list of post "tags", with a number next to each of how many posts have used that tag, and ordered by the number of posts.我正在构建一个 CRUD 应用程序并尝试显示一个帖子“标签”列表,每个帖子使用该标签的次数旁边都有一个数字,并按帖子数量排序。 I have one table for posts, one for tags, and one join table called posts_tags.
我有一张用于帖子的表,一张用于标签的表,还有一张名为 posts_tags 的连接表。 When I execute the query I think should do the trick, it displays the count of all rows of the posts_tags table instead of just the count of rows associated with each tag.
当我执行我认为应该解决问题的查询时,它会显示 posts_tags 表的所有行数,而不仅仅是与每个标签关联的行数。 In the image below, the "test" tag has been used on 3 posts and "test 2" on 1 (which are the numbers that should show up next to them), but as you can see I get 4 instead:
在下图中,“test”标签已用于 3 个帖子,“test 2”用于 1 个(这是应该显示在它们旁边的数字),但如您所见,我得到的是 4:
display of incorrect post counts for tags显示错误的标签帖子计数
My tags table has a relationship with the posts_tags table, allowing me to use "Tag.tagged_post_ids" in the query:我的标签表与 posts_tags 表有关系,允许我在查询中使用“Tag.tagged_post_ids”:
` `
class Tag(db.Model):
""" Model for tags table """
__tablename__ = "tags"
id = db.Column(
db.Integer,
primary_key=True,
autoincrement=True
)
tag = db.Column(
db.String(30),
nullable=False,
unique=True
)
description = db.Column(
db.Text,
nullable=False
)
tagged_post_ids = db.relationship(
"PostTag"
)
` `
Here's the SQLA query I wrote:这是我写的 SQLA 查询:
` `
tags = db.session.query(Tag.tag, func.count(Tag.tagged_post_ids).label("count")).group_by(Tag.tag).order_by(func.count(Tag.tagged_post_ids)).all()
` `
I have successfully built the query in SQL:我在 SQL 中成功构建了查询:
SELECT tags.tag, COUNT(posts_tags.post_id) FROM tags JOIN posts_tags ON posts_tags.tag_id = tags.id GROUP BY tags.tag ORDER BY COUNT(posts_tags.post_id) DESC;
My main issue is trying to translate this into SQLAlchemy. I feel like my query is a 1-to-1 for my SQL query, but it's not working.我的主要问题是试图将其转换为 SQLAlchemy。我觉得我的查询对于 SQL 查询是一对一的,但它不起作用。 Any help would be greatly appreciated.
任何帮助将不胜感激。
EDIT: Adding my Post model and PostTag (join) model:编辑:添加我的帖子 model 和 PostTag(加入)model:
class Post(db.Model):
""" Model for posts table """
__tablename__ = "posts"
id = db.Column(
db.Integer,
primary_key=True,
autoincrement=True
)
user_id = db.Column(
db.Integer,
db.ForeignKey("users.id")
)
title = db.Column(
db.Text,
nullable=False
)
content = db.Column(
db.Text
)
url = db.Column(
db.Text
)
img_url = db.Column(
db.Text
)
created_at = db.Column(
db.DateTime,
nullable=False,
default=db.func.now()
)
score = db.Column(
db.Integer,
nullable=False,
default=0
)
tags = db.relationship(
"Tag",
secondary="posts_tags",
backref="posts"
)
comments = db.relationship(
"Comment",
backref="post"
)
@property
def tag_list(self):
""" Builds comma separated list of tags for the post. """
tag_list = []
for tag in self.tags:
tag_list.append(tag.tag)
return tag_list
class PostTag(db.Model):
""" Model for join table between posts and tags """
__tablename__ = "posts_tags"
post_id = db.Column(
db.Integer,
db.ForeignKey("posts.id"),
primary_key=True
)
tag_id = db.Column(
db.Integer,
db.ForeignKey("tags.id"),
primary_key=True
)
If you are using backref
you only need to define one side of the relationship.如果您使用的是
backref
,则只需定义关系的一侧。 I actually don't know what happens when you use func.count
on a relationship
, I only use it on a column.我实际上不知道当你在
relationship
上使用func.count
时会发生什么,我只在列上使用它。 Here are a couple options.这里有几个选项。 An outer join is needed to catch the case when there are 0 posts with that tag otherwise with an inner join that tag will just be missing from the result.
当有 0 个带有该标签的帖子时,需要一个外部连接来捕获这种情况,否则对于一个内部连接,该标签只会从结果中丢失。 I also use
func.coalesce
to convert NULL
to 0
in the first example.在第一个示例中,我还使用
func.coalesce
将NULL
转换为0
。
class Tag(Base):
""" Model for tags table """
__tablename__ = "tags"
id = Column(
Integer,
primary_key=True,
autoincrement=True
)
tag = Column(
String(30),
nullable=False,
unique=True
)
# Redundant
# tagged_post_ids = relationship(
# "PostTag"
# )
class Post(Base):
""" Model for posts table """
__tablename__ = "posts"
id = Column(
Integer,
primary_key=True,
autoincrement=True
)
title = Column(
Text,
nullable=False
)
tags = relationship(
"Tag",
secondary="posts_tags",
backref="posts"
)
@property
def tag_list(self):
""" Builds comma separated list of tags for the post. """
tag_list = []
for tag in self.tags:
tag_list.append(tag.tag)
return tag_list
class PostTag(Base):
""" Model for join table between posts and tags """
__tablename__ = "posts_tags"
post_id = Column(
Integer,
ForeignKey("posts.id"),
primary_key=True
)
tag_id = Column(
Integer,
ForeignKey("tags.id"),
primary_key=True
)
metadata.create_all(engine)
with Session(engine) as session, session.begin():
# With subquery
tag_subq = select(
PostTag.tag_id,
func.count(PostTag.post_id).label("post_count")
).group_by(
PostTag.tag_id
).order_by(
func.count(PostTag.post_id)
).subquery()
q = session.query(
Tag.tag,
func.coalesce(tag_subq.c.post_count, 0)
).outerjoin(
tag_subq,
Tag.id == tag_subq.c.tag_id
).order_by(
func.coalesce(tag_subq.c.post_count, 0))
for (tag_name, post_count) in q.all():
print (tag_name, post_count)
# With join
q = session.query(
Tag.tag,
func.count(PostTag.post_id).label('post_count')
).outerjoin(
PostTag,
Tag.id == PostTag.tag_id
).group_by(
Tag.id
).order_by(
func.count(PostTag.post_id))
for (tag_name, post_count) in q.all():
print (tag_name, post_count)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.