ORM

Django ORMで合計値を取得|aggregateとSumの使い方

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つの辞書で返す点