如何将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')
这段代码的逻辑很清晰:
- 先用
values('pid')按pid完成分组 - 对每个状态,用
Case/When判断当前记录的status是否匹配,匹配则返回1,否则默认返回None 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
相关产品推荐
相关产品推荐

