如何让Django查询返回含当前年份(无数据时显示0)的最近3年交易统计
如何让Django查询返回含当前年份(无数据时显示0)的最近3年交易统计
这个需求我太有共鸣了——之前做后台报表的时候也碰到过类似问题,默认查询只会返回有交易数据的年份,空年份直接就被忽略了,报表展示起来特别不连贯。咱们可以分几步来解决,思路很清晰:先明确我们要的三个目标年份,再把数据库里的统计数据和这些年份做合并,没有数据的年份直接补0就行。
第一步:生成我们需要的目标年份列表
首先得拿到当前年份,然后生成从当前年往前推2年的三个年份(比如2025、2024、2023)。用Python的datetime模块就能轻松搞定:
from datetime import datetime current_year = datetime.now().year # 生成[当前年, 当前年-1, 当前年-2]的列表 target_years = [current_year - i for i in range(3)]
第二步:查询目标年份内的已有交易统计
接下来调整你的原查询,只在这三个目标年份里捞数据,这样能减少不必要的数据库查询量。这里我推荐用ExtractYear代替TruncYear,因为它直接返回年份数字,后续合并数据会更方便:
from django.db.models import Sum from django.db.models.functions import ExtractYear # 查询目标年份的交易统计 yearly_stats = ( Transactions.objects .annotate(year=ExtractYear('timestamp')) # 直接提取年份数字 .filter(year__in=target_years) # 只查我们要的三个年份 .values('year') .annotate(total=Sum('amount')) )
第三步:合并数据,补全年份的0值
把查询结果转成字典,这样我们可以快速通过年份找到对应的统计值。然后遍历我们的目标年份列表,有数据的就用数据库查出来的total,没数据的直接设为0:
# 把查询结果转成{年份: 总金额}的字典 stats_map = {item['year']: item['total'] for item in yearly_stats} # 生成最终的结果列表 final_result = [ {'year': year, 'total': stats_map.get(year, 0)} for year in target_years ]
这样得到的final_result就完全符合你的需求了——不管2025年有没有交易数据,都会显示{'year':2025, 'total':0},同时按2025、2024、2023的顺序排列。
如果你更习惯用TruncYear(返回的是当年1月1日的日期对象),也可以稍微调整一下代码:
from datetime import date from django.db.models import Sum, TruncYear # 生成目标年份的日期对象(比如2025-01-01) target_year_dates = [date(current_year - i, 1, 1) for i in range(3)] # 用TruncYear查询 yearly_stats = ( Transactions.objects .annotate(year=TruncYear('timestamp')) .filter(year__in=target_year_dates) .values('year') .annotate(total=Sum('amount')) ) stats_map = {item['year']: item['total'] for item in yearly_stats} final_result = [ {'year': year, 'total': stats_map.get(year, 0)} for year in target_year_dates ]
这个方法的好处是逻辑简单易懂,而且只查我们需要的年份,性能上也没什么问题,完全适合你要的最近3年的场景。
备注:内容来源于stack exchange,提问作者donbolli
相关产品推荐
相关产品推荐

