DjangoのORMで、関連するモデルのレコード数を各レコードに追加するには、annotate()とCount()を組み合わせます。例えば「各カテゴリの記事数」や「各部署の社員数」などを効率的に取得できます。
基本的な使い方
views.py
from django.db.models import Count
model = Company.objects.all().annotate(
persons=Count('person')
).values()
print(model)
説明
Step 1Count関数の基本
Djangoでは、Count関数を使用して関連するモデルの数を数えることができます。基本的な構文は以下の通りです:
from django.db.models import Count Count(数えたいフィールド)
この関数は、annotateやaggregateメソッドと組み合わせて使用します。
Step 2基本的な使用例
例えば、Companyモデルに紐づいているPersonモデルの数を数える場合:
from django.db.models import Count
# 各会社に紐づくPersonの数をカウント
companies = Company.objects.annotate(persons_count=Count('persons'))
上の例は、自身に紐づいたPersonモデルの数量をpersons_countフィールドに代入しています。
Step 3フィルタリングと組み合わせる
Count関数はフィルタリングと組み合わせることができます:
# 社員が5人以上の会社だけを取得
companies = Company.objects.annotate(
persons_count=Count('persons')
).filter(persons_count__gte=5)
# 特定の部署の社員数をカウント
companies = Company.objects.annotate(
dev_count=Count('persons', filter=Q(persons__department='開発部'))
)
filterパラメータを使うことで、カウント対象を特定の条件に一致するものだけに限定できます。
Step 4distinct引数を使う
重複を除外してカウントする場合は、distinct=Trueを指定します:
# 各会社にある部署の数(重複を除く)
companies = Company.objects.annotate(
department_count=Count('persons__department', distinct=True)
)
# 各会社が取引している顧客の数(重複を除く)
companies = Company.objects.annotate(
customer_count=Count('projects__customer', distinct=True)
)
distinctを使うことで、同じ値が複数回出現しても1つとしてカウントされるようになります。
Step 5複数のカウントを同時に行う
1つのクエリで複数の集計を行うことができます:
# 各会社の社員数と部署数を同時に取得
companies = Company.objects.annotate(
persons_count=Count('persons'),
department_count=Count('departments')
)
# 部署ごとの男性社員数と女性社員数
departments = Department.objects.annotate(
male_count=Count('persons', filter=Q(persons__gender='男性')),
female_count=Count('persons', filter=Q(persons__gender='女性'))
)
Step 6実践的な使用例
views.pyでのCount関数の使用例:
from django.shortcuts import render
from django.db.models import Count, Q
from .models import Company, Department
def company_statistics(request):
# 会社ごとの統計情報を計算
companies = Company.objects.annotate(
# 全社員数
total_employees=Count('persons'),
# 部署ごとの社員数
dev_employees=Count('persons', filter=Q(persons__department='開発部')),
sales_employees=Count('persons', filter=Q(persons__department='営業部')),
admin_employees=Count('persons', filter=Q(persons__department='管理部')),
# 部署数(重複を除外)
department_count=Count('persons__department', distinct=True)
).order_by('-total_employees')
return render(request, 'companies/statistics.html', {
'companies': companies
})
def department_comparison(request):
# 部署ごとの統計
departments = Department.objects.annotate(
employee_count=Count('persons'),
male_ratio=Count('persons', filter=Q(persons__gender='男性')) * 100.0 / Count('persons'),
project_count=Count('persons__projects', distinct=True)
).order_by('-employee_count')
return render(request, 'departments/comparison.html', {
'departments': departments
})
テンプレートでの使用例(statistics.html):
<h1>会社統計</h1>
<table>
<tr>
<th>会社名</th>
<th>総社員数</th>
<th>開発部</th>
<th>営業部</th>
<th>管理部</th>
<th>部署数</th>
</tr>
{% for company in companies %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.total_employees }}人</td>
<td>{{ company.dev_employees }}人</td>
<td>{{ company.sales_employees }}人</td>
<td>{{ company.admin_employees }}人</td>
<td>{{ company.department_count }}部署</td>
</tr>
{% endfor %}
</table>
重要ポイント:
- Count関数は、Django ORMのクエリ内で数を集計するために使用します。
- annotateと組み合わせると、各レコードに集計フィールドを追加できます。
- filterパラメータを使うと、特定条件に一致するものだけをカウントできます。
- distinct=Trueを指定すると、重複する値を除外してカウントできます。
- 複数のCount関数を組み合わせることで、1つのクエリで複数の集計を行うことができます。
まとめ
annotate(Count())で関連データの件数を各レコードに追加できるCount()の引数にリレーション名を指定するdistinct=Trueで重複を除いたカウントが可能filter引数やQオブジェクトで条件付きカウントができるorder_by()と組み合わせてカウント順にソートできる