ORM

Django ORMで計算フィールドを追加|annotate()の使い方

DjangoのORMで、QuerySetの各レコードに計算結果などの新しいフィールドを追加するには、annotate()メソッドを使用します。集計関数やF式と組み合わせて、データベース側で効率的に計算できます。

基本的な使い方

views.py
model = Person.objects.all().annotate(
    next_age=F('age') + 1
).values()

説明

Step 1annotateメソッドの基本

Djangoでは、annotateメソッドを使用して、クエリ結果に一時的なフィールドを追加することができます。基本的な構文は以下の通りです:

モデル.objects.annotate(追加するフィールド名=追加するデータ)

annotateを使用しても取得したデータに対してフィールドを追加するだけなので、元のモデルにフィールドが増えるわけではありません。

Step 2基本的な使用例

例えば、Personモデルのageフィールドに1を足した値を新しいフィールドとして追加する場合:

from django.db.models import F

# ageフィールドに1を足した値をnext_ageフィールドとして追加
persons = Person.objects.annotate(next_age=F('age') + 1)

上の例では、Personモデルにannotateでnext_ageフィールドにageフィールドの値+1した値を追加しています。

Step 3様々な計算の例

annotateメソッドでは、様々な計算や集計ができます:

from django.db.models import F, Sum, Count, Avg, Max, Min

# 数値演算
products = Product.objects.annotate(
    discounted_price=F('price') * 0.9,  # 10%割引価格
    price_with_tax=F('price') * 1.1     # 税込価格
)

# 文字列連結(PostgreSQLの場合)
from django.db.models.functions import Concat
from django.db.models import Value
persons = Person.objects.annotate(
    full_name=Concat('first_name', Value(' '), 'last_name')
)

Step 4集計関数との組み合わせ

関連モデルの集計値を注釈として追加することも可能です:

# 会社ごとの社員数を追加
companies = Company.objects.annotate(employee_count=Count('persons'))

# 会社ごとの平均年齢を追加
companies = Company.objects.annotate(avg_age=Avg('persons__age'))

# 会社ごとの最高年齢と最低年齢を追加
companies = Company.objects.annotate(
    max_age=Max('persons__age'),
    min_age=Min('persons__age')
)

# 会社ごとの部署数を追加
companies = Company.objects.annotate(department_count=Count('departments', distinct=True))

Step 5フィルタリングと組み合わせる

annotateで追加したフィールドを使ってフィルタリングすることもできます:

# 社員数が10人以上の会社を取得
companies = Company.objects.annotate(
    employee_count=Count('persons')
).filter(employee_count__gte=10)

# 平均年齢が30歳以上の会社を取得
companies = Company.objects.annotate(
    avg_age=Avg('persons__age')
).filter(avg_age__gte=30)

# 追加フィールドでソート
companies = Company.objects.annotate(
    employee_count=Count('persons')
).order_by('-employee_count')  # 社員数の多い順

Step 6実践的な使用例

views.pyでのannotateメソッドの使用例:

from django.shortcuts import render
from django.db.models import Count, Avg, F
from .models import Company, Person

def company_statistics(request):
    # 会社ごとの統計情報を計算
    companies = Company.objects.annotate(
        employee_count=Count('persons'),
        avg_age=Avg('persons__age'),
        next_year_avg_age=Avg('persons__age') + 1
    ).order_by('-employee_count')
    
    return render(request, 'companies/statistics.html', {
        'companies': companies
    })

def product_pricing(request):
    # 割引率をパラメータから取得
    discount_rate = float(request.GET.get('discount', 0)) / 100
    
    # 標準価格と割引価格を計算
    products = Product.objects.annotate(
        price_with_tax=F('price') * 1.1,
        discounted_price=F('price') * (1 - discount_rate)
    )
    
    return render(request, 'products/pricing.html', {
        'products': products,
        'discount_rate': discount_rate * 100
    })

テンプレートでの使用例(statistics.html):

<h1>会社統計</h1>

<table>
    <tr>
        <th>会社名</th>
        <th>社員数</th>
        <th>平均年齢</th>
        <th>来年の平均年齢</th>
    </tr>
    {% for company in companies %}
        <tr>
            <td>{{ company.name }}</td>
            <td>{{ company.employee_count }}人</td>
            <td>{{ company.avg_age|floatformat:1 }}歳</td>
            <td>{{ company.next_year_avg_age|floatformat:1 }}歳</td>
        </tr>
    {% endfor %}
</table>
重要ポイント:
  • annotateは一時的なフィールドを追加するため、データベースに変更は加えられません。
  • 複雑な計算をデータベース側で行うことができるため、パフォーマンスの向上につながります。
  • F式やデータベース関数を使用することで、より高度な計算が可能です。
  • 複数のannotateを連鎖させると、各annotateの結果を次のannotateで使用できます。

まとめ

  • annotate()でQuerySetの各レコードに計算フィールドを追加できる
  • Count, Sum, Avg, Max, Minなどの集計関数と組み合わせて使う
  • 追加したフィールドはfilter()order_by()の条件にも使える
  • F式と組み合わせてフィールド同士の演算結果を追加できる
  • すべての計算はデータベース側で行われるため効率的