Django如何统计每位医生月度及全时段预约记录并排序
实现思路
基于给出的Django模型结构,通过ORM聚合函数即可完成要求的排序、统计需求,不需要手写原生SQL。
首先附上参考模型(已修正代码转义字符):
class Doctors(models.Model): title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True, db_index=True, verbose_name="URL") content = models.TextField(blank=True) photo = models.ImageField(upload_to="photos/%Y/%m/%d") time_create = models.DateTimeField(auto_now_add=True) time_update = models.DateTimeField(auto_now=True) spec = models.ForeignKey('Speciality', on_delete=models.PROTECT, null=True) class Appointment(models.Model): class Meta: unique_together = ('title', 'date', 'timeslot') TIMESLOT_LIST = ( (0, '09:00 - 09:30'), (1, '10:00 - 10:30'), (2, '11:00 - 11:30'), (3, '12:00 - 12:30'), (4, '13:00 – 13:30'), (5, '14:00 – 14:30'), (6, '15:00 – 15:30'), (7, '16:00 – 16:30'), (8, '17:00 – 17:30'), ) title = models.ForeignKey('Doctors', on_delete=models.CASCADE) date = models.DateField() timeslot = models.IntegerField(choices=TIMESLOT_LIST) username = models.ForeignKey(User, max_length=60, on_delete=models.CASCADE) complaints = models.CharField(max_length=500) phone = models.CharField(max_length=30)
注意:现有Appointment模型中关联Doctors的外键命名为
title存在歧义,该字段实际存储的是医生模型实例,不是职称/名称文本,后续迭代建议迁移改名为doctor提升代码可读性,以下示例暂时沿用现有字段名编写。
1. 统计每位医生全周期预约总记录数
通过annotate配合反向关联的聚合查询即可,一次查询就能给所有医生对象附加总预约数字段:
from django.db.models import Count # 查询所有医生,附加总预约数属性 doctor_list = Doctors.objects.annotate( total_appointment_count = Count('appointment') ) # 取值示例 # for doc in doctor_list: # print(f"医生{doc.title}总预约量:{doc.total_appointment_count}")
2. 月度预约统计+排序
使用TruncMonth函数将预约日期截断到月份维度,分组聚合后即可得到每位医生每月的预约量,支持自定义排序规则:
from django.db.models import Count from django.db.models.functions import TruncMonth monthly_stats = Appointment.objects.annotate( # 把预约日期截断到月份,格式如2024-06-01 stat_month = TruncMonth('date') ).values( 'title_id', # 关联的医生ID 'title__title', # 医生名称(对应Doctors表的title字段) 'stat_month' ).annotate( monthly_count = Count('id') # 统计该医生当月的预约数 ).order_by( 'title_id', # 优先按医生ID分组排序 '-stat_month', # 同一医生的记录按月份倒序,最新月份在前 '-monthly_count' # 如需按月度预约量倒序保留该规则即可,不需要可以删除 )
如果只需要统计指定月份的所有医生预约量,可以加过滤条件:
# 示例:统计2024年6月的医生预约量 june_2024_stats = Appointment.objects.filter( date__year=2024, date__month=6 ).values('title_id', 'title__title').annotate( monthly_count = Count('id') ).order_by('-monthly_count') # 按当月预约量从高到低排序
3. 合并统计:一次查询同时返回总预约数、指定月预约数
如果需要在医生列表页同时展示总预约量、当月预约量并排序,可以用子查询避免多次查库:
from django.db.models import Count, OuterRef, Subquery from datetime import date # 构造当月预约量子查询 current_month = date.today().month current_year = date.today().year current_month_count_subq = Appointment.objects.filter( title_id=OuterRef('pk'), date__year=current_year, date__month=current_month ).values('title_id').annotate(cnt=Count('id')).values('cnt') # 最终查询结果 doctor_rank_list = Doctors.objects.annotate( total_count = Count('appointment'), current_month_count = Subquery(current_month_count_subq, default=0) # 当月无预约默认返回0 ).order_by('-current_month_count') # 按月度预约量倒序排列
补充说明
- 排序规则可以根据业务需求调整
order_by内的字段:字段名前加-为倒序,不加为正序 - 如果需要获取单个医生的月度预约明细,只需要在查询时增加
title_id=目标医生ID的过滤条件即可 - 大数据量场景下建议给
Appointment表的title、date字段加联合索引,提升统计查询速度
内容的提问来源于stack exchange,提问作者William55
相关产品推荐
相关产品推荐

