Betrachten Sie einfache Django-Modelle Event
und 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)
Es ist einfach, Ereignisabfragen mit der Gesamtzahl der Teilnehmer zu kommentieren:
events = Event.objects.all().annotate(participants=models.Count('participant'))
Wie kommentiere ich mit der Anzahl der Teilnehmer, nach denen gefiltert wurde is_paid=True
?
Ich muss alle Ereignisse unabhängig von der Anzahl der Teilnehmer abfragen , z. B. muss ich nicht nach kommentierten Ergebnissen filtern. Wenn es 0
Teilnehmer gibt, ist das in Ordnung, ich brauche nur einen 0
kommentierten Wert.
Das Beispiel aus der Dokumentation funktioniert hier nicht, da Objekte von der Abfrage ausgeschlossen werden, anstatt sie mit Anmerkungen zu versehen 0
.
Aktualisieren. Django 1.8 verfügt über eine neue Funktion für bedingte Ausdrücke. Jetzt können wir Folgendes tun:
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()
)))
Update 2. Django 2.0 verfügt über eine neue Funktion zur bedingten Aggregation (siehe die akzeptierte Antwort unten).
aggregate
Verwendung wird angezeigt. Haben Sie solche Abfragen bereits getestet? (Ich habe nicht und ich möchte glauben! :)Wir haben gerade festgestellt, dass Django 1.8 über eine neue Funktion für bedingte Ausdrücke verfügt. Jetzt können wir Folgendes tun:
quelle
Count
(anstelle vonSum
) verwenden, sollten wir es setzendefault=None
(wenn nicht dasfilter
Argument django 2 verwendet wird ).AKTUALISIEREN
Der von mir erwähnte Unterabfrage- Ansatz wird jetzt in Django 1.11 über Unterabfrage-Ausdrücke unterstützt .
Ich bevorzuge dies gegenüber der Aggregation (Summe + Fall) , da es schneller und einfacher zu optimieren sein sollte (bei richtiger Indizierung) .
Für ältere Versionen kann das gleiche mit erreicht werden
.extra
quelle
.extra
, als hätte ich den Weg ohne Verwendung gefunden , da ich SQL in Django lieber meide :) Ich werde die Frage aktualisieren.Django 1.8.2
, also denke ich, dass Sie mit dieser Version sind und deshalb funktioniert es für Sie. Sie können hier und hierNone
auch. Meine Lösung warCoalesce
(from django.db.models.functions import Coalesce
) zu verwenden. Sie verwenden es so :Coalesce(Subquery(...), 0)
. Es kann jedoch einen besseren Ansatz geben.Ich würde vorschlagen, stattdessen die
.values
Methode IhresParticipant
Abfragesets zu verwenden.Kurz gesagt, was Sie tun möchten, ist gegeben durch:
Ein vollständiges Beispiel lautet wie folgt:
Erstellen Sie 2
Event
s:Fügen Sie
Participant
ihnen s hinzu:Gruppieren Sie alle
Participant
s nach ihremevent
Feld:Hier ist etwas Besonderes erforderlich:
Was
.values
und.distinct
tun hier ist, dass sie zwei Eimer vonParticipant
s erstellen, die nach ihrem Element gruppiert sindevent
. Beachten Sie, dass diese Eimer enthaltenParticipant
.Sie können diese Buckets dann mit Anmerkungen versehen, da sie den Originalsatz enthalten
Participant
. Hier wollen wir die Anzahl zählenParticipant
, dies geschieht einfach durch Zählen derid
s der Elemente in diesen Buckets (da diese sindParticipant
):Schließlich möchten Sie nur
Participant
mit einemis_paid
WesenTrue
, Sie können einfach einen Filter vor dem vorherigen Ausdruck hinzufügen, und dies ergibt den oben gezeigten Ausdruck:Der einzige Nachteil ist, dass Sie das
Event
später abrufen müssen, da Sie nur dasid
von der obigen Methode haben.quelle
Welches Ergebnis suche ich:
Im Allgemeinen müsste ich zwei verschiedene Abfragen verwenden:
Aber ich möchte beides in einer Abfrage. Daher:
Ergebnis:
quelle