You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django按分类统计最热帖子时distinct与annotation联用报错如何解决

问题原因

当前代码失效主要是三个问题导致的:

  1. 多对多关联计数重复:Question和Category是多对多关系,未指定distinct=True的Count操作会让同一条点赞/点踩记录被多次统计,结果不准
  2. 分组逻辑不匹配:先调用values_list('category__title','title')会让ORM按「分类标题+帖子标题」两个字段组合分组,而非按分类维度分组,导致后续distinct去重逻辑失效
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 01:24:07