Django按分类统计最热帖子时distinct与annotation联用报错如何解决
问题原因
当前代码失效主要是三个问题导致的:
- 多对多关联计数重复:Question和Category是多对多关系,未指定
distinct=True的Count操作会让同一条点赞/点踩记录被多次统计,结果不准 - 分组逻辑不匹配:先调用
values_list('category__title','title')会让ORM按「分类标题+帖子标题」两个字段组合分组,而非按分类维度分组,导致后续distinct去重逻辑失效 - DISTINCT ON语法约束:PostgreSQL要求
distinct()中的字段必须和order_by()的第一个排序字段完全一致,你当前写法中两个位置的字段未严格对齐
正确查询代码
from django.db.models import Count, F, Q def get_queryset(self): return Question.objects.values( 'category__id', 'category__title', 'id', 'title' ).annotate( l=Count('likedislike', filter=Q(likedislike__point=1), distinct=True), d=Count('likedislike', filter=Q(likedislike__point=0), distinct=True), total=F('l') + F('d') ).order_by( 'category__id', '-total' ).distinct('category__id')
代码修改说明
- 新增
category__id作为分组和去重的核心字段,避免分类重名导致的统计错误 - Count操作新增
distinct=True,消除多对多关联带来的重复计数问题 order_by第一个排序字段和distinct字段严格对齐为category__id,符合PostgreSQL DISTINCT ON语法要求- 保留了帖子id字段,方便后续序列化时关联其他帖子属性
兼容非PostgreSQL数据库的写法
如果后续需要切换回MySQL等其他数据库,可以用窗口函数Rank实现相同效果,不需要依赖DISTINCT ON语法:
from django.db.models import Count, F, Q, Window, Rank from django.db.models.functions import Coalesce def get_queryset(self): queryset = Question.objects.values( 'category__id', 'category__title', 'id', 'title' ).annotate( l=Count('likedislike', filter=Q(likedislike__point=1), distinct=True), d=Count('likedislike', filter=Q(likedislike__point=0), distinct=True), total=Coalesce(F('l') + F('d'), 0), rank=Window( expression=Rank(), partition_by=F('category__id'), order_by=F('total').desc() ) ).filter(rank=1) return queryset
这个写法会给每个分类下的帖子按互动量排名,过滤出排名第一的帖子即可,兼容所有支持窗口函数的数据库(MySQL 8.0+、PostgreSQL等)。
内容的提问来源于stack exchange,提问作者kokiwebaaa
相关产品推荐
相关产品推荐

