Django ORM:用窗口函数分组获取每日首末烘焙蛋糕月度报告
生成月度蛋糕报告的Django ORM实现方案
我来给你梳理下怎么用Django ORM搞定这个月度蛋糕报告需求——要拿到当月每一天最早和最晚烘焙的蛋糕名称对吧?咱们一步步来:
1. 先准备必要的导入
首先得把需要的模块和函数导进来:
from django.db.models import Subquery, OuterRef from django.db.models.functions import TruncDate from datetime import datetime, timedelta
2. 确定当月的时间范围
先算出当月的起始和结束时间,确保咱们只查询当月的数据:
today = datetime.today() # 当月第一天的0点 start_of_month = today.replace(day=1, hour=0, minute=0, second=0, microsecond=0) # 当月最后一天的23:59:59 end_of_month = (start_of_month + timedelta(days=32)).replace(day=1) - timedelta(seconds=1)
3. 编写子查询获取每天的第一个和最后一个蛋糕
因为我们需要的是对应时间点的蛋糕名称,而不是单纯的时间值,所以用子查询来关联每个日期的最早/最晚记录:
# 获取当天第一个烘焙的蛋糕名称 first_cake_subquery = Cake.objects.filter( baked_on__date=OuterRef('baked_on__date'), baked_on__gte=start_of_month, baked_on__lte=end_of_month ).order_by('baked_on').values('cake_name')[:1] # 获取当天最后一个烘焙的蛋糕名称 last_cake_subquery = Cake.objects.filter( baked_on__date=OuterRef('baked_on__date'), baked_on__gte=start_of_month, baked_on__lte=end_of_month ).order_by('-baked_on').values('cake_name')[:1]
4. 主查询生成月度报告
通过TruncDate把baked_on截断到日期维度,然后分组聚合得到每天的结果:
monthly_report = Cake.objects.filter( baked_on__gte=start_of_month, baked_on__lte=end_of_month ).annotate( # 提取日期部分,用于分组 date=TruncDate('baked_on') ).values('date').annotate( # 关联子查询得到第一个和最后一个蛋糕名称 first_baked_cake=Subquery(first_cake_subquery), last_baked_cake=Subquery(last_cake_subquery) ).order_by('date')
5. 使用查询结果
你可以直接遍历monthly_report来获取数据,每条结果的结构大概是这样的:
for day_report in monthly_report: print(f"日期: {day_report['date'].strftime('%m月%d日')}") print(f"第一个烘焙的蛋糕: {day_report['first_baked_cake']}") print(f"最后一个烘焙的蛋糕: {day_report['last_baked_cake']}") print("---")
补充说明
- 如果某天只有一个蛋糕,那么
first_baked_cake和last_baked_cake会是同一个名称,完全符合需求; TruncDate是Django提供的跨数据库兼容函数,能稳定地把datetime字段转换为日期维度;- 子查询里的
[:1]确保只取排序后的第一条记录,配合升序/降序排序就能精准拿到最早或最晚的蛋糕。
内容的提问来源于stack exchange,提问作者Anomitra
相关产品推荐
相关产品推荐

