Tortoise ORM如何按ManyToManyField关联项数量实现过滤查询
问题原因
Tortoise-ORM 中直接对 ManyToMany 多对多字段、ReverseRelation 反向关联字段添加过滤条件时,ORM默认会去匹配关联模型本身的字段属性,不会自动统计关联条目的总数量,因此原代码中likes__gt=0、comments__gt=0的写法无法生效,会抛出字段不存在的错误。
实现方案
核心逻辑是通过annotate()结合ORM内置的聚合函数,先为每个帖子对象计算出点赞数、评论数的统计字段,再基于这两个生成的统计字段完成过滤、排序操作。
- 先导入需要的依赖
from tortoise.functions import Count from datetime import datetime, timedelta
- 编写查询逻辑
# 提前计算7天前的时间阈值,避免重复运算 week_ago = datetime.now() - timedelta(days=7) posts = Post.annotate( # 统计关联点赞总数,distinct=True用于去重,避免联表产生的笛卡尔积导致计数不准 likes_count=Count("likes", distinct=True), # 统计关联评论总数 comments_count=Count("comments", distinct=True) ).filter( likes_count__gt=0, comments_count__gt=0, created_at__gt=week_ago, filter__lte=user.config.filter ) # 按热门规则排序,示例为优先按点赞数降序、再按评论数降序,可根据业务调整 .order_by("-likes_count", "-comments_count")
优化参考
- 如果需要自定义热门评分规则(比如设置点赞、评论的不同权重),可以用
RawSQL自定义聚合计算字段,参考写法:
from tortoise.expressions import RawSQL posts = Post.annotate( likes_count=Count("likes", distinct=True), comments_count=Count("comments", distinct=True), # 示例规则:1次点赞计2分,1次评论计3分,按总分排序 hot_score=RawSQL("COUNT(DISTINCT like_user.user_id)*2 + COUNT(DISTINCT comment.id)*3") ).filter( likes_count__gt=0, comments_count__gt=0, created_at__gt=week_ago, filter__lte=user.config.filter ).order_by("-hot_score")
- 数据量较大时,建议为
created_at字段、点赞关联表/评论表的外键字段添加数据库索引,大幅降低联表聚合查询的耗时。 - 如果需要分页返回热门内容,直接在查询链末尾追加
.offset(skip).limit(limit)即可,聚合统计会在分页前完成计算,不会出现计数错误。
内容的提问来源于stack exchange,提问作者ConchDev
相关产品推荐
相关产品推荐

