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

如何基于Django模型按类别统计账单金额总和?

问题描述

我有一个存储账单数据的表,每条账单都被归类,例如信用卡、贷款、个人账单等。我正在尝试统计每个类别的总金额。

我的Django模型定义如下:

class Debt(models.Model):
    Categories = [
        ('Credit Card', 'Credit Card'),
        ('Mortgage', 'Mortgage'),
        ('Loan', 'Loan'),
        ('Household Bill', 'Household Bill'),
        ('Social', 'Social'),
        ('Personal', 'Personal')
    ]
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    creditor = models.ForeignKey(Creditor, on_delete=models.CASCADE)
    amount = models.FloatField(blank=True, null=True)
    category = models.CharField(max_length=50, blank=True, null=True, choices=Categories)

我认为使用annotate是正确的方法,但不知道如何按类别分组来统计总额。目前的代码只能统计单个类别:

all_outgoing = Debt.objects.annotate(total=Sum('amount')).filter(category='personal')

我不想为每个类别单独写annotate,也不确定循环遍历类别做过滤查询是不是最优方案,想知道更好的实现方式。

解决方案

不需要循环每个类别单独查询,Django ORM可以通过values()与annotate()结合,一次性完成按类别分组的金额统计,这种方式仅需一次数据库请求,比循环查询效率高很多。

基础实现

通过values('category')指定分组字段,再用annotate()计算每个分组的金额总和:

from django.db.models import Sum

category_totals = Debt.objects.values('category').annotate(total=Sum('amount'))

返回的QuerySet中每个元素是字典,格式示例:{'category': 'Credit Card', 'total': 1500.0},包含所有类别及其对应的总金额。

优化细节

  1. 过滤无效数据:如果存在category为空或amount为空的记录,可提前过滤避免统计无效值:
category_totals = Debt.objects.exclude(category__isnull=True)\
                              .exclude(amount__isnull=True)\
                              .values('category')\
                              .annotate(total=Sum('amount'))
  1. 按用户筛选:若需统计特定用户的账单,添加用户过滤条件即可:
# 假设request.user为当前登录用户
category_totals = Debt.objects.filter(user=request.user)\
                              .exclude(category__isnull=True)\
                              .exclude(amount__isnull=True)\
                              .values('category')\
                              .annotate(total=Sum('amount'))
  1. 结果排序:可按总金额或类别排序,比如按总金额降序排列(空值排在最后):
from django.db.models import Sum, F

category_totals = Debt.objects.values('category')\
                              .annotate(total=Sum('amount'))\
                              .order_by(F('total').desc(nulls_last=True))

为什么不推荐循环查询

循环遍历每个类别单独查询会触发多次数据库请求,随着数据量增长,性能损耗会越来越明显。而分组统计只需要一次数据库查询,在性能上远优于循环方案。

内容的提问来源于stack exchange,提问作者JacksWastedLife

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 07:35:11