Django中高效统计不同状态用户数量的最优实现方法
Django User模型多状态统计优化方案
问题描述
在Django项目中,需要统计User模型(角色为role=2)不同状态的用户数量。当前尝试通过多次annotate实现,但希望找到更简便的单次查询方案,避免多次调用count()产生多查询,同时优化现有写法。
当前实现代码:
workers = get_user_model().objects.annotate( not_active_workers=Count("id", filter=Q(is_active=False, status=0)) ).annotate( active_not_complete_workers=Count("id", filter=Q(is_active=True, profile_completed=False, status=0)) ).annotate( complete_applicant_workers=Count("id", filter=Q(is_active=True, profile_completed=True, status=0)) ).annotate( former_workers=Count("id", filter=Q(is_active=False, status=2)) ).annotate( accepted_workers=Count("id", filter=Q(is_active=False, status=1)) ).filter(role=2) dict( not_active=workers[0].not_active_workers, active_not_complete=workers[0].active_not_complete_workers, complete_applicant=workers[0].complete_applicant_workers, former=workers[0].former_workers, accepted=workers[0].accepted_workers, )
优化方案1:使用aggregate结合Count+filter(最简洁)
你的现有写法是可行的,但因为不需要分组统计(仅针对role=2的所有用户做全局统计),用aggregate替代annotate更贴合需求,无需取查询集第一个元素,直接返回统计结果字典:
from django.db.models import Count, Q from django.contrib.auth import get_user_model User = get_user_model() stats = User.objects.filter(role=2).aggregate( not_active=Count("id", filter=Q(is_active=False, status=0)), active_not_complete=Count("id", filter=Q(is_active=True, profile_completed=False, status=0)), complete_applicant=Count("id", filter=Q(is_active=True, profile_completed=True, status=0)), former=Count("id", filter=Q(is_active=False, status=2)), accepted=Count("id", filter=Q(is_active=False, status=1)) ) # stats直接就是目标字典,示例输出:{'not_active': 5, 'active_not_complete': 12, ...}
优化方案2:使用Case+When+Sum
如果偏好更直观的条件判断逻辑,也可以用Case和When配合Sum实现,同样是单次查询:
from django.db.models import Case, When, IntegerField, Sum from django.contrib.auth import get_user_model User = get_user_model() stats = User.objects.filter(role=2).aggregate( not_active=Sum( Case( When(is_active=False, status=0, then=1), default=0, output_field=IntegerField() ) ), active_not_complete=Sum( Case( When(is_active=True, profile_completed=False, status=0, then=1), default=0, output_field=IntegerField() ) ), complete_applicant=Sum( Case( When(is_active=True, profile_completed=True, status=0, then=1), default=0, output_field=IntegerField() ) ), former=Sum( Case( When(is_active=False, status=2, then=1), default=0, output_field=IntegerField() ) ), accepted=Sum( Case( When(is_active=False, status=1, then=1), default=0, output_field=IntegerField() ) ) )
方案对比
- 原写法:通过
annotate生成带统计字段的查询集,再取第一个元素的值,本质是单次查询,但annotate为分组统计设计,用aggregate更适配全局统计场景。 aggregate+Count方案:代码最简洁,Django自动生成单次SQL查询,性能最优。Case+Sum方案:逻辑更直观,适合复杂条件的统计场景,同样是单次查询。
所有优化方案都仅产生1次数据库查询,比多次调用count()(产生5次查询)效率更高。
内容的提问来源于stack exchange,提问作者Spirconi
相关产品推荐
相关产品推荐

