
本文详解如何在 django 中使用 aggregate() 和 sum 聚合函数,对模型字段(如每日记录的 lift_frame)进行求和,并在模板中清晰展示各列累计值,避免手动 sql 或外部库依赖。
本文详解如何在 django 中使用 aggregate() 和 sum 聚合函数,对模型字段(如每日记录的 lift_frame)进行求和,并在模板中清晰展示各列累计值,避免手动 sql 或外部库依赖。
在 Django Web 应用中,常需对时间序列数据(如每日作业记录)进行汇总统计——例如统计整月“lift_frame 在服务天数”的总和。与其依赖 Pandas 或原生 SQL 查询,Django ORM 提供了简洁、安全且可维护的聚合方案:django.db.models.Sum 与 aggregate() 方法。
✅ 推荐做法:使用 aggregate() + Sum 实现高效聚合
1. 单字段求和(如仅显示 lift_frame 总和)
修改 views.py,引入 Sum 并在视图中计算:
from django.db.models import Sum
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.shortcuts import render
from .models import Day
class Dayboard(LoginRequiredMixin, View):
def get(self, request):
days = Day.objects.all().order_by('day')
# 计算 lift_frame 字段总和,若无数据则返回 0
lift_sum = Day.objects.aggregate(
total_lift_frame=Sum('lift_frame')
)['total_lift_frame'] or 0
return render(request, 'days/dayboard.html', {
'days': days,
'lift_sum': lift_sum
})
在 dayboard.html 模板底部(或表格下方合适位置)添加显示:
<div class="mt-4 p-3 bg-light rounded">
<strong>Total Lift Frame Days:</strong> {{ lift_sum }}
</div>
2. 多字段批量求和(推荐用于生产环境)
为提升复用性与可维护性,建议将聚合逻辑封装至模型层。在 models.py 的 Day 类中添加类方法:
from django.db.models import Sum
class Day(models.Model):
day = models.DateField(auto_now=False)
well_name = models.ForeignKey("Well", on_delete=models.SET_NULL, null=True)
lift_frame = models.IntegerField(default=0)
mpd_manifold_building = models.IntegerField(default=0)
rcd_housing = models.IntegerField(default=0)
pipework = models.IntegerField(default=0)
mpd_supervisor = models.IntegerField(default=2)
mpd_operator = models.IntegerField(default=2)
def __str__(self):
return self.day.strftime("%d %B")
@classmethod
def get_totals(cls):
"""返回所有关键字段的聚合总和,None 值自动转为 0"""
fields = [
"lift_frame",
"mpd_manifold_building",
"rcd_housing",
"pipework",
"mpd_supervisor",
"mpd_operator",
]
# 动态构建聚合字典:total_lift_frame → Sum('lift_frame')
aggregates = {f"total_{f}": Sum(f) for f in fields}
result = cls.objects.aggregate(**aggregates)
return {k: (v or 0) for k, v in result.items()}
更新 views.py,调用该方法:
class Dayboard(LoginRequiredMixin, View):
def get(self, request):
days = Day.objects.all().order_by('day')
totals = Day.get_totals() # 一行获取全部总和
return render(request, 'days/dayboard.html', {
'days': days,
'totals': totals
})
在 dayboard.html 表格底部添加汇总行(注意使用 colspan 对齐):
<tfoot><tr class="table-secondary font-weight-bold">
<td colspan="2">Totals</td>
<td>{{ totals.total_mpd_manifold_building }}</td>
<td>{{ totals.total_rcd_housing }}</td>
<td>{{ totals.total_pipework }}</td>
<td>{{ totals.total_lift_frame }}</td>
<td>{{ totals.total_mpd_supervisor }}</td>
<td>{{ totals.total_mpd_operator }}</td>
<td></td>
</tr></tfoot>
? 关键优势说明
- ✅ 零数据库往返开销:aggregate() 在单次查询中完成所有求和,比多次 .filter().count() 或循环累加更高效;
- ✅ 类型安全 & NULL 容错:Sum 自动处理空值,配合 or 0 或模型层封装可确保模板始终渲染数字;
- ✅ 解耦清晰:聚合逻辑置于模型,视图专注流程控制,模板专注渲染,符合 Django “Fat Model, Thin View” 哲学;
- ❌ 避免反模式:无需导入 pandas 或手动连接 SQLite(如原 main.py 所示),既减少依赖,又规避并发/事务不一致风险。
最后,请确保在
后添加 (而非 内部),以保证语义化 HTML 结构与样式兼容性。部署前建议对 Day.objects.aggregate(...) 添加单元测试,验证空数据集返回 0 的健壮性。











