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

如何将Django annotate+Count的聚合结果转为按pid分组结构?

解决Django QuerySet按pid分组并将status转为键值对的问题

嘿,这个需求其实就是典型的行转列操作,在Django ORM里可以通过Case和When结合聚合函数来实现,我给你两种实用方案:

方案一:已知固定的status值(硬编码实现)

如果你的status字段只有Completed、Hold、InProgress、New这几个固定值,直接用annotate结合Case/When逐个统计每个状态的数量即可:

from django.db.models import Count, Case, When, IntegerField

tasks = Task.objects.values('pid').annotate(
    # 统计Completed状态的任务数
    Completed=Count(Case(
        When(status='Completed', then=1),
        output_field=IntegerField()
    )),
    # 统计Hold状态的任务数
    Hold=Count(Case(
        When(status='Hold', then=1),
        output_field=IntegerField()
    )),
    # 统计InProgress状态的任务数
    InProgress=Count(Case(
        When(status='InProgress', then=1),
        output_field=IntegerField()
    )),
    # 统计New状态的任务数
    New=Count(Case(
        When(status='New', then=1),
        output_field=IntegerField()
    ))
).order_by('pid')

这段代码的逻辑很清晰:

  1. 先用values('pid')按pid完成分组
  2. 对每个状态,用Case/When判断当前记录的status是否匹配,匹配则返回1,否则默认返回None
  3. Count函数会统计每个分组内返回1的记录数,也就是对应状态的任务数量

执行后得到的QuerySet就是你期望的结构:

<QuerySet [{'pid': 11, 'Completed': 3, 'Hold': 12, 'InProgress': 2, 'New': 3}, ...]>

方案二:动态适配所有status值(扩展性更好)

如果以后可能新增其他status值,不想每次都修改代码,可以先获取所有不重复的status值,再动态生成annotate的参数:

from django.db.models import Count, Case, When, IntegerField

# 先获取所有不重复的status选项
status_options = Task.objects.values_list('status', flat=True).distinct()

# 构建annotate所需的关键字参数字典
annotate_params = {}
for status in status_options:
    annotate_params[status] = Count(Case(
        When(status=status, then=1),
        output_field=IntegerField()
    ))

# 执行查询
tasks = Task.objects.values('pid').annotate(**annotate_params).order_by('pid')

这样不管后续新增多少种status,代码都能自动适配,生成对应的统计字段。

内容的提问来源于stack exchange,提问作者Krishna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 18:30:58