Django ORMのcount入門
データの件数を数える方法
Django ORMでデータの件数を効率的に数える方法を解説します。count()、exists()、aggregateの使い分けを紹介します。
こんな人向けの記事です
- Django ORMでデータの件数を取得したい人
- count()とlen()の違いを知りたい人
- 条件付きカウントやグループごとのカウントを学びたい人
Step 1count()の基本
QuerySetのcount()メソッドは、SQLのSELECT COUNT(*)を発行してデータの件数を取得します。
Python
# models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
category = models.CharField(max_length=50)
price = models.IntegerField()
is_active = models.BooleanField(default=True)
def __str__(self):
return self.namePython
# 全件数を取得
total = Product.objects.count()
print(f"全商品数: {total}")
# 条件を指定して件数を取得
active_count = Product.objects.filter(is_active=True).count()
print(f"アクティブ商品: {active_count}")
# カテゴリごとの件数
food_count = Product.objects.filter(category="食品").count()
print(f"食品: {food_count}")実行結果
全商品数: 50
アクティブ商品: 42
食品: 15Python
# count() vs len() の違い
# count(): SQLのCOUNTを使う(効率的)
count = Product.objects.filter(is_active=True).count()
# SQL: SELECT COUNT(*) FROM product WHERE is_active = True
# len(): 全データをPythonに読み込んでから数える(非効率)
count = len(Product.objects.filter(is_active=True))
# SQL: SELECT * FROM product WHERE is_active = True
# → 全レコードをメモリに読み込むlen()とcount()の使い分け
件数だけが必要な場合は必ずcount()を使いましょう。len()は全データをメモリに読み込むため、大量データでは非常に遅くなります。ただし、既にQuerySetを評価済み(ループ後など)の場合はlen()でも構いません。Step 2exists()で存在チェック
データが1件以上存在するかどうかだけを知りたい場合は、exists()が最も効率的です。
Python
# exists(): 1件でも存在すればTrue(最も高速)
has_products = Product.objects.filter(category="食品").exists()
print(f"食品あり: {has_products}")
# BAD: count()で存在チェック(遅い)
if Product.objects.filter(category="食品").count() > 0:
print("食品があります")
# BAD: bool()で存在チェック(全件取得してしまう)
if Product.objects.filter(category="食品"):
print("食品があります")
# GOOD: exists()で存在チェック
if Product.objects.filter(category="食品").exists():
print("食品があります")実行結果
食品あり: True
食品がありますexists()の内部動作
exists()はSQLのSELECT 1 FROM ... LIMIT 1を発行します。1件見つかった時点で即座にTrueを返すため、count()よりも高速です。Step 3条件付きカウント
Python
from django.db.models import Q
# 複数条件でのカウント
expensive_active = Product.objects.filter(
is_active=True,
price__gte=10000
).count()
print(f"高額アクティブ商品: {expensive_active}")
# OR条件でのカウント
food_or_drink = Product.objects.filter(
Q(category="食品") | Q(category="飲料")
).count()
print(f"食品または飲料: {food_or_drink}")
# 除外してカウント
non_food = Product.objects.exclude(category="食品").count()
print(f"食品以外: {non_food}")
# NULLの件数
null_count = Product.objects.filter(category__isnull=True).count()
not_null_count = Product.objects.filter(category__isnull=False).count()Step 4aggregateとannotateでの集計
グループごとのカウントや、複数のカウントを一度に取得する方法です。
Python
from django.db.models import Count, Q
# aggregate: 全体の集計値を辞書で取得
result = Product.objects.aggregate(
total=Count("id"),
active=Count("id", filter=Q(is_active=True)),
inactive=Count("id", filter=Q(is_active=False)),
)
print(f"全体: {result['total']}")
print(f"アクティブ: {result['active']}")
print(f"非アクティブ: {result['inactive']}")実行結果
全体: 50
アクティブ: 42
非アクティブ: 8Python
# annotate: グループごとの集計(GROUP BY)
category_counts = Product.objects.values("category").annotate(
count=Count("id")
).order_by("-count")
for item in category_counts:
print(f"{item['category']}: {item['count']}件")
# 関連モデルのカウント
from .models import Department
departments = Department.objects.annotate(
emp_count=Count("employees")
).order_by("-emp_count")
for dept in departments:
print(f"{dept.name}: {dept.emp_count}人")
# 条件付きの関連カウント
departments = Department.objects.annotate(
total=Count("employees"),
active=Count("employees", filter=Q(employees__is_active=True)),
)
for dept in departments:
print(f"{dept.name}: {dept.active}/{dept.total}人")実行結果
食品: 15件
電子機器: 12件
衣料品: 10件
飲料: 8件
その他: 5件Step 5パフォーマンスの注意点
Python
# 1. 同じQuerySetを複数回count()するとSQLも複数回発行される
# BAD:
total = Product.objects.count() # SQL発行
active = Product.objects.filter(is_active=True).count() # SQL発行
# → 2回SQLが発行される
# GOOD: aggregateで1回にまとめる
result = Product.objects.aggregate(
total=Count("id"),
active=Count("id", filter=Q(is_active=True)),
)
# → 1回のSQLで済む
# 2. テンプレートでのカウント
# BAD: テンプレート内で{{ products|length }}を使う
# GOOD: ビューでcount()してcontextに渡す
def product_list(request):
products = Product.objects.filter(is_active=True)
return render(request, "product/list.html", {
"products": products,
"total": products.count(), # ビューでカウント
})
# 3. distinct()でユニークカウント
unique_categories = Product.objects.values("category").distinct().count()
print(f"カテゴリ数: {unique_categories}")まとめ
count()はSQLのCOUNTを使うためlen()より効率的- 存在チェックだけなら
exists()が最も高速 aggregate()で複数のカウントを1回のSQLにまとめられるannotate()とCount()でグループごとのカウントができるCount(filter=Q(...))で条件付きカウントが可能