繁体   English   中英

SQLAlchemy 查询返回所有连接行的计数,不按连接行分组

[英]SQLAlchemy query returning count of ALL join rows, not grouping by joined row

我正在构建一个 CRUD 应用程序并尝试显示一个帖子“标签”列表,每个帖子使用该标签的次数旁边都有一个数字,并按帖子数量排序。 我有一张用于帖子的表,一张用于标签的表,还有一张名为 posts_tags 的连接表。 当我执行我认为应该解决问题的查询时,它会显示 posts_tags 表的所有行数,而不仅仅是与每个标签关联的行数。 在下图中,“test”标签已用于 3 个帖子,“test 2”用于 1 个(这是应该显示在它们旁边的数字),但如您所见,我得到的是 4:

显示错误的标签帖子计数

我的标签表与 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"
    )

`

这是我写的 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()

`

我在 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;

我的主要问题是试图将其转换为 SQLAlchemy。我觉得我的查询对于 SQL 查询是一对一的,但它不起作用。 任何帮助将不胜感激。

编辑:添加我的帖子 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
    )

如果您使用的是backref ,则只需定义关系的一侧。 我实际上不知道当你在relationship上使用func.count时会发生什么,我只在列上使用它。 这里有几个选项。 当有 0 个带有该标签的帖子时,需要一个外部连接来捕获这种情况,否则对于一个内部连接,该标签只会从结果中丢失。 在第一个示例中,我还使用func.coalesceNULL转换为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM