如何在Django QuerySet中按年月分组并包含0计数的月份
补全Django QuerySet中缺失月份的统计数据
要实现包含无数据月份(count为0)的统计,Django ORM无法直接生成空行,需要手动生成时间范围内的所有年月组合,再与现有统计结果合并。以下是具体实现步骤:
1. 确定统计的时间边界
首先获取数据库中PaymentRequest记录的最早和最晚arrival_date对应的年月,以此确定需要覆盖的时间范围:
from datetime import datetime from django.db.models import Min, Max, Extract, Count # 获取时间边界 date_bounds = PaymentRequest.objects.aggregate( min_year=Min(Extract('arrival_date', 'year')), min_month=Min(Extract('arrival_date', 'month')), max_year=Max(Extract('arrival_date', 'year')), max_month=Max(Extract('arrival_date', 'month')) ) # 处理无数据的情况,默认取最近一年 if not all(date_bounds.values()): now = datetime.now() min_year, min_month = now.year - 1, now.month max_year, max_month = now.year, now.month else: min_year, min_month = date_bounds['min_year'], date_bounds['min_month'] max_year, max_month = date_bounds['max_year'], date_bounds['max_month']
2. 生成时间范围内的所有年月组合
遍历从起始年月到结束年月的所有月份,生成完整的年月列表:
all_months = [] current_year, current_month = min_year, min_month while (current_year, current_month) <= (max_year, max_month): all_months.append({'year': current_year, 'month': current_month}) # 月份递增,处理跨年逻辑 if current_month == 12: current_year += 1 current_month = 1 else: current_month += 1
3. 获取原统计结果并转换为字典
将原QuerySet的统计结果转换为以(year, month)为键的字典,方便快速查找:
# 执行原统计查询 original_stats = PaymentRequest.objects.annotate( year=Extract('arrival_date', 'year'), month=Extract('arrival_date', 'month') ).values('year', 'month').annotate(count=Count('*')) # 转换为字典,便于匹配 stats_map = {(item['year'], item['month']): item['count'] for item in original_stats}
4. 合并结果并补全缺失月份
遍历所有年月组合,匹配统计数据,缺失的月份设count=0,最后按需求排序:
full_stats = [] for month in all_months: key = (month['year'], month['month']) full_stats.append({ 'year': month['year'], 'month': month['month'], 'count': stats_map.get(key, 0) }) # 按年份降序、月份降序排序 full_stats.sort(key=lambda x: (-x['year'], -x['month']))
最终full_stats就是包含所有月份的统计结果,缺失的月份会显示count=0,比如你提到的2022年12月会被补全为{'year': 2022, 'month': 12, 'count': 0}。
内容的提问来源于stack exchange,提问作者haduki
相关产品推荐
相关产品推荐

