You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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)

需要生成按月份统计支出总和的表格,示例格式如下:

MonthTotal
2023/11200
2023/12400

尝试的查询代码未实现聚合效果,每条支付记录对应一行,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:00:06