如何在Django中通过日期字段获取近6个月月度总费用数据
获取近6个月月度总费用的实现方案
步骤1:计算时间范围
先确定统计的时间区间——近6个月(包含当月),用datetime和python-dateutil的relativedelta精准计算起始日期,确保覆盖完整的6个自然月:
from datetime import datetime from dateutil.relativedelta import relativedelta today = datetime.today() # 取当前月第一天,再往前推5个月,得到6个月前的当月第一天 start_date = (today.replace(day=1) - relativedelta(months=5)).date()
步骤2:ORM分组聚合查询
利用Django ORM的筛选、分组和聚合功能,按年月分组计算每个月的总费用:
from django.db.models import Sum, F from .models import Visits # 筛选时间范围内的数据,按年、月分组并统计总费用 monthly_data = Visits.objects.filter( visit_date__gte=start_date ).annotate( year=F('visit_date__year'), month=F('visit_date__month') ).values('year', 'month').annotate( total_fees=Sum('fees') ).order_by('-year', '-month')
按year和month分组是为了避免跨年的同名月份(如2023年7月和2024年7月)被混淆。
步骤3:转换为目标格式
将查询结果转换成以月份名称为键的字典,借助calendar模块获取月份名称:
import calendar # 基础转换(仅包含有数据的月份) result = {} for item in monthly_data: month_name = calendar.month_name[item['month']] result[month_name] = item['total_fees']
可选:补全无数据的月份
如果需要即使某个月份没有访问记录,也显示总费用为0,可以先生成近6个月的完整月份列表,再填充数据:
# 生成近6个月的年、月列表(按从新到旧排序) months_list = [] current_year, current_month = today.year, today.month for i in range(6): calc_month = current_month - i calc_year = current_year if calc_month <= 0: calc_month += 12 calc_year -= 1 months_list.append((calc_year, calc_month)) # 初始化所有月份总费用为0 full_result = {calendar.month_name[m[1]]: 0 for m in months_list} # 填充已有数据 for item in monthly_data: month_name = calendar.month_name[item['month']] full_result[month_name] = item['total_fees'] # 保持月份从新到旧的顺序 ordered_result = {} for year, month in months_list: ordered_result[calendar.month_name[month]] = full_result[calendar.month_name[month]]
最终效果
运行后ordered_result会输出类似需求的格式:
{ 'July': Decimal('750.00'), 'June': Decimal('800.00'), 'May': Decimal('500.00'), 'April': Decimal('200.00'), 'March': Decimal('450.00'), 'February': Decimal('310.00') }
注:fees是DecimalField类型,结果会是Decimal对象,若需要转成整数可使用int(item['total_fees'])(前提是费用均为整数)。
内容的提问来源于stack exchange,提问作者Saravanan Ragavan
相关产品推荐
相关产品推荐

