DjangoのORMでフィールドの合計値を計算するには、aggregate()とSum()を組み合わせます。関連モデルの合計や条件付きの集計など、データ分析に欠かせない機能です。
基本的な使い方
views.py
from django.db.models import Sum
model = Person.objects.all().annotate(
sale =Sum('sales__sale')
).values()
print(model)
説明
Step 1Sum関数の基本
Djangoでは、Sum関数を使用して関連するモデルのフィールド値を合計することができます。基本的な構文は以下の通りです:
from django.db.models import Sum
Sum('合計したいフィールド')
この関数は、annotateやaggregateメソッドと組み合わせて使用します。
Step 2基本的な使用例
例えば、Companyモデルに紐づいているSalesモデルのsaleフィールドの合計値を計算する場合:
from django.db.models import Sum
# 各会社の売上合計を計算
companies = Company.objects.annotate(total_sales=Sum('sales__sale'))
上の例は、自身に紐づいたSalesモデルのsaleフィールドの値を合計してtotal_salesフィールドに代入しています。
Step 3フィルタリングと組み合わせる
Sum関数はフィルタリングと組み合わせることができます:
# 合計売上が100万以上の会社だけを取得
companies = Company.objects.annotate(
total_sales=Sum('sales__sale')
).filter(total_sales__gte=1000000)
# 2023年の売上合計を計算
from django.db.models import Sum, Q
from datetime import date
companies = Company.objects.annotate(
sales_2023=Sum('sales__sale',
filter=Q(sales__date__year=2023))
)
filterパラメータを使うことで、合計対象を特定の条件に一致するものだけに限定できます。
Step 4複数のSum集計を同時に行う
1つのクエリで複数の合計計算を行うことができます:
# 各会社の年ごとの売上合計を計算
companies = Company.objects.annotate(
sales_2021=Sum('sales__sale', filter=Q(sales__date__year=2021)),
sales_2022=Sum('sales__sale', filter=Q(sales__date__year=2022)),
sales_2023=Sum('sales__sale', filter=Q(sales__date__year=2023))
)
# 商品カテゴリ別の売上合計を計算
companies = Company.objects.annotate(
product_a_sales=Sum('sales__sale', filter=Q(sales__product_category='A')),
product_b_sales=Sum('sales__sale', filter=Q(sales__product_category='B')),
product_c_sales=Sum('sales__sale', filter=Q(sales__product_category='C'))
)
Step 5数式を含む合計計算
Sum関数と他の計算式を組み合わせることもできます:
from django.db.models import Sum, F
# 税込売上の合計を計算
companies = Company.objects.annotate(
total_sales_with_tax=Sum(F('sales__sale') * 1.1)
)
# コスト差し引き後の利益を計算
companies = Company.objects.annotate(
total_profit=Sum(F('sales__sale') - F('sales__cost'))
)
F式を使うことで、複数のフィールドを組み合わせた計算が可能になります。
Step 6実践的な使用例
views.pyでのSum関数の使用例:
from django.shortcuts import render
from django.db.models import Sum, Q
from .models import Company, Sales
from datetime import date
def sales_dashboard(request):
# 現在の年を取得
current_year = date.today().year
# 会社ごとの売上統計を計算
companies = Company.objects.annotate(
# 総売上
total_sales=Sum('sales__sale'),
# 今年の売上
current_year_sales=Sum('sales__sale',
filter=Q(sales__date__year=current_year)),
# 四半期ごとの売上(今年)
q1_sales=Sum('sales__sale',
filter=Q(sales__date__year=current_year) &
Q(sales__date__month__in=[1, 2, 3])),
q2_sales=Sum('sales__sale',
filter=Q(sales__date__year=current_year) &
Q(sales__date__month__in=[4, 5, 6])),
q3_sales=Sum('sales__sale',
filter=Q(sales__date__year=current_year) &
Q(sales__date__month__in=[7, 8, 9])),
q4_sales=Sum('sales__sale',
filter=Q(sales__date__year=current_year) &
Q(sales__date__month__in=[10, 11, 12])),
).order_by('-total_sales')
# 全社の合計売上
total_company_sales = Company.objects.aggregate(
total=Sum('sales__sale')
)['total'] or 0
return render(request, 'companies/sales_dashboard.html', {
'companies': companies,
'total_company_sales': total_company_sales,
'current_year': current_year
})
テンプレートでの使用例(sales_dashboard.html):
<h1>売上ダッシュボード</h1>
<p>全社合計売上: {{ total_company_sales|floatformat:0 }}円</p>
<h2>{{ current_year }}年の会社別売上</h2>
<table>
<tr>
<th>会社名</th>
<th>総売上</th>
<th>今年の売上</th>
<th>Q1</th>
<th>Q2</th>
<th>Q3</th>
<th>Q4</th>
</tr>
{% for company in companies %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.total_sales|floatformat:0 }}円</td>
<td>{{ company.current_year_sales|floatformat:0 }}円</td>
<td>{{ company.q1_sales|floatformat:0 }}円</td>
<td>{{ company.q2_sales|floatformat:0 }}円</td>
<td>{{ company.q3_sales|floatformat:0 }}円</td>
<td>{{ company.q4_sales|floatformat:0 }}円</td>
</tr>
{% endfor %}
</table>
重要ポイント:
- Sum関数は、数値フィールドの合計を計算するために使用します。
- NULLの値は合計に含まれません。
- 集計結果がないまたはすべての値がNULLの場合、Sum関数はNoneを返します。
- annotateメソッドと組み合わせると、各レコードに合計フィールドを追加できます。
- aggregateメソッドと組み合わせると、クエリセット全体の合計値を一つだけ取得できます。
- filterパラメータを使うと、特定条件のレコードだけを合計対象にできます。
まとめ
aggregate(Sum())でフィールドの合計値を辞書形式で取得できる- 結果は
{'フィールド名__sum': 値}の形式で返される filter()と組み合わせて条件付きの合計を算出できる- 関連モデルのフィールドも
__で指定して集計可能 annotate()との違いは、QuerySet全体の集計結果を1つの辞書で返す点