Django ORMでフィールドの合計値を取得する方法を解説します。aggregate()メソッドとSum関数を使うと、SQLのSUM()に相当する集計をPythonコードで簡単に実行できます。
基本的な使い方
フィールドの合計値を取得するには、aggregate()とSumを組み合わせます。
from django.db.models import Sum
from .models import Sale
def index(request):
# amountフィールドの合計を取得
result = Sale.objects.aggregate(Sum('amount'))
print(result){'amount__sum': 1250000}aggregate()は辞書を返します。キーはフィールド名__関数名の形式です。カスタムキー名を指定することもできます。
# カスタムキー名を指定
result = Sale.objects.aggregate(total=Sum('amount'))
print(result) # {'total': 1250000}
print(result['total']) # 1250000条件付きの合計
filter()と組み合わせると、条件に一致するレコードだけの合計を計算できます。
from django.db.models import Sum
from datetime import date
# 今月の売上合計
result = Sale.objects.filter(
sales_date__year=date.today().year,
sales_date__month=date.today().month
).aggregate(total=Sum('amount'))
# 特定カテゴリの売上合計
result = Sale.objects.filter(
product_category="電化製品"
).aggregate(total=Sum('amount'))
# 特定の顧客の購入合計
result = Sale.objects.filter(
customer_id=5
).aggregate(total=Sum('amount'))複数の集計を同時に取得
aggregate()には複数の集計関数を同時に渡せます。
from django.db.models import Sum, Avg, Count, Min, Max
result = Sale.objects.aggregate(
total=Sum('amount'),
average=Avg('amount'),
count=Count('id'),
min_amount=Min('amount'),
max_amount=Max('amount')
)
print(result){'total': 1250000, 'average': 25000.0, 'count': 50, 'min_amount': 1000, 'max_amount': 150000}グループごとの合計(annotate)
グループ化して合計を取得するには、values()とannotate()を組み合わせます。
from django.db.models import Sum
# カテゴリごとの売上合計
category_totals = Sale.objects.values('product_category').annotate(
total=Sum('amount')
).order_by('-total')
for item in category_totals:
print(f"{item['product_category']}: {item['total']}円")電化製品: 500000円
家具: 350000円
食品: 200000円
書籍: 120000円
衣類: 80000円values()でグループ化のキーを指定し、annotate()で各グループに集計値を付与します。SQLのGROUP BYに相当します。
関連モデルの合計
関連モデルのフィールドを集計することもできます。
from django.db.models import Sum
# 各顧客の購入合計金額を取得
customers_with_total = Customer.objects.annotate(
total_purchases=Sum('sale__amount')
).order_by('-total_purchases')
for customer in customers_with_total:
print(f"{customer.name}: {customer.total_purchases}円")annotate()を使うと、各オブジェクトに集計値が属性として追加されます。テンプレートでも{{ customer.total_purchases }}のようにアクセスできます。
実践的な使用例
売上ダッシュボードを構築する実践例です。
from django.shortcuts import render
from django.db.models import Sum, Count, Avg
from datetime import date, timedelta
from .models import Sale
def sales_dashboard(request):
today = date.today()
first_of_month = today.replace(day=1)
last_month_start = (first_of_month - timedelta(days=1)).replace(day=1)
# 総売上
total_sales = Sale.objects.aggregate(
total=Sum('amount')
)['total'] or 0
# 今月の売上
monthly_sales = Sale.objects.filter(
sales_date__gte=first_of_month
).aggregate(total=Sum('amount'))['total'] or 0
# 前月の売上
prev_month_sales = Sale.objects.filter(
sales_date__gte=last_month_start,
sales_date__lt=first_of_month
).aggregate(total=Sum('amount'))['total'] or 0
# カテゴリ別売上
category_sales = Sale.objects.values(
'product_category'
).annotate(
total=Sum('amount'),
count=Count('id'),
avg=Avg('amount')
).order_by('-total')
return render(request, 'sales/dashboard.html', {
'total_sales': total_sales,
'monthly_sales': monthly_sales,
'prev_month_sales': prev_month_sales,
'category_sales': category_sales,
})aggregate()はクエリセット全体の集計結果を辞書で返し、annotate()は各オブジェクトに集計値を属性として付与します。全体の合計が欲しい場合はaggregate()、グループごとの合計が欲しい場合はvalues().annotate()を使い分けましょう。
aggregate()の結果で対象レコードが0件の場合、SumはNoneを返します。or 0を付けるか、Sum('amount', default=0)(Django 4.0以降)を使ってNone対策を行いましょう。
まとめ
aggregate(Sum('field'))でフィールドの合計値を辞書として取得できるSum以外にもAvg、Count、Min、Maxが使えるvalues().annotate()でグループごとの集計ができる(GROUP BY相当)annotate()は各オブジェクトに集計値を属性として追加する- 対象が0件の場合に
Noneが返るため、or 0やdefault=0で対策する