如何用Django ORM按月份聚合支付金额总和?
按月份统计Django Payment模型支出总和的正确ORM写法
问题背景
现有Django模型:
class Payment(models.Model): paid_date = models.DateField("Paid on") amount = models.DecimalField(max_digits=7, decimal_places=2)
需要生成按月份统计支出总和的表格,示例格式如下:
| Month | Total |
|---|---|
| 2023/11 | 200 |
| 2023/12 | 400 |
尝试的查询代码未实现聚合效果,每条支付记录对应一行,amount__sum值与amount完全一致:
exp_by_month = Payment.objects \ .annotate(year_mo=Trunc('paid_date', 'month')) \ .values('year_mo', 'amount') \ .annotate(Sum('amount'))
错误原因
查询中values同时包含year_mo和amount,会让Django按**year_mo+amount的组合**分组,相当于每条支付记录单独成组,自然无法实现按月聚合的效果。
正确写法
基础聚合查询
只需在values中保留分组依据的year_mo,再对amount求和即可:
from django.db.models import Sum from django.db.models.functions import Trunc exp_by_month = Payment.objects \ .annotate(year_mo=Trunc('paid_date', 'month')) \ .values('year_mo') \ .annotate(total=Sum('amount')) \ .order_by('year_mo')
Trunc('paid_date', 'month'):将paid_date截断到当月第一天(比如2023-11-15转为2023-11-01),作为分组的唯一依据。values('year_mo'):指定仅按year_mo字段分组。annotate(total=Sum('amount')):计算每个分组的金额总和,用total作为结果字段名。order_by('year_mo'):保证结果按月份顺序排列。
格式化月份为2023/11格式
如果需要直接得到示例中的月份显示格式,可以通过ExtractYear、ExtractMonth结合字符串拼接实现:
from django.db.models import Sum, Value from django.db.models.functions import ExtractYear, ExtractMonth, Concat from django.db import models exp_by_month = Payment.objects \ .annotate( year=ExtractYear('paid_date'), month=ExtractMonth('paid_date') ) \ .annotate( month_label=Concat( 'year', Value('/'), 'month', output_field=models.CharField() ) ) \ .values('month_label') \ .annotate(total=Sum('amount')) \ .order_by('year', 'month')
查询结果中的month_label字段直接是2023/11格式,可直接用于表格渲染。
结果使用示例
遍历查询结果生成表格内容:
for item in exp_by_month: print(f"{item['month_label']}: {item['total']}")
内容的提问来源于stack exchange,提问作者JasonTiller
相关产品推荐
相关产品推荐

