シンプルな Django モデルを考えてみEvent
ましょうParticipant
:
class Event(models.Model):
title = models.CharField(max_length=100)
class Participant(models.Model):
event = models.ForeignKey(Event, db_index=True)
is_paid = models.BooleanField(default=False, db_index=True)
参加者の総数でイベントクエリに注釈を付けるのはとても簡単です:
events = Event.objects.all().annotate(participants=models.Count('participant'))
参加者の数をフィルターして注釈を付けるにはどうすればいいですかis_paid=True
?
問い合わせが必要ですすべてのイベント参加者の数に関係なく、たとえば、注釈付きの結果でフィルタリングする必要はありません。参加者がいる場合は問題ありません。注釈付きの値0
だけが必要です。0
のドキュメントからの例はここでは機能しません。これは、オブジェクトに を注釈として付けるのではなく、クエリからオブジェクトを除外するためです0
。
アップデート。Django 1.8には新しい条件式機能なので、次のようにすることができます:
events = Event.objects.all().annotate(paid_participants=models.Sum(
models.Case(
models.When(participant__is_paid=True, then=1),
default=0,
output_field=models.IntegerField()
)))
アップデート2。Django 2.0には新しい条件付き集計機能については、受け入れられた答え以下。これはDjango 3.xでも動作します。
ベストアンサー1
条件付き集計Django 2.0 以降では、これまでのような面倒な作業をさらに減らすことができます。また、Postgres のfilter
ロジックも使用されます。これは、合計ケースよりもいくらか高速です (20 ~ 30% といった数字が飛び交っているのを見たことがあります)。
とにかく、あなたの場合、次のような単純なものを検討しています:
from django.db.models import Q, Count
events = Event.objects.annotate(
paid_participants=Count('participants', filter=Q(participants__is_paid=True))
)
ドキュメントには別のセクションがあります注釈のフィルタリングこれは条件付き集計と同じものですが、上記の例に似ています。どちらにしても、これは以前行っていた厄介なサブクエリよりもはるかに健全です。