求助:Django ORM能否通过单查询实现用户班次数量统计?
一次查询统计用户班次数量的解决方案
嘿,同为Django老玩家,太懂这种卡一整天的憋屈了!咱们直接来解决这个问题,先假设你的模型结构大概是这样(如果和实际有出入,你可以调整相关字段和关联名称):
from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): pass class Day(models.Model): date = models.DateField() # 主管与Day的M2M关联,设置related_name方便反向查询 supervisors = models.ManyToManyField(User, related_name='supervised_days') class Shift(models.Model): day = models.ForeignKey(Day, on_delete=models.CASCADE, related_name='shifts') # 人员与Shift的M2M关联 workers = models.ManyToManyField(User, related_name='worked_shifts')
要在一次查询里统计每位用户的总班次数量(包含作为员工参与的班次、作为主管关联的班次),咱们可以利用Django的annotate结合Count和Q对象实现:
from django.db.models import Count, Q # 统计每位用户的总班次数量 user_shift_counts = User.objects.annotate( total_shifts=Count( 'worked_shifts', distinct=True # 避免同一班次被重复计数 ) + Count( 'supervised_days__shifts', distinct=True # 确保主管关联的班次只统计一次 ) ).values('username', 'total_shifts')
核心逻辑解释:
Count('worked_shifts', distinct=True):统计用户作为员工直接参与的Shift数量,加distinct=True是为了防止极端情况下(比如数据异常)同一班次被重复计数。Count('supervised_days__shifts', distinct=True):通过反向关联找到用户主管的所有Day,再关联到这些Day下的所有Shift,统计总数,同样用distinct=True保证每个Shift只被计数一次。- 两个Count结果相加,就是用户的总班次数量。
如果你的需求只是统计用户作为员工参与的班次,那简化版代码如下:
user_worker_counts = User.objects.annotate( worked_shift_count=Count('worked_shifts', distinct=True) ).values('username', 'worked_shift_count')
要是需要过滤特定时间段的班次,直接在查询前加filter即可,比如统计2024年全年的班次:
from datetime import date start_date = date(2024, 1, 1) end_date = date(2024, 12, 31) filtered_counts = User.objects.annotate( total_shifts=Count( 'worked_shifts', filter=Q(worked_shifts__day__date__range=(start_date, end_date)), distinct=True ) + Count( 'supervised_days__shifts', filter=Q(supervised_days__date__range=(start_date, end_date)), distinct=True ) ).values('username', 'total_shifts')
这样全程用Django ORM就能搞定,完全不用写raw SQL,性能也能得到保障!
内容的提问来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

