Ich habe zwei Modelle A
und B
. Alle B
Objekte haben einen Fremdschlüssel für ein A
Objekt. Gibt es bei einer Reihe von A
Objekten ohnehin die Möglichkeit, mit dem ORM eine Reihe von B
Objekten abzurufen, die das zuletzt für jedes A
Objekt erstellte Objekt enthalten.
Hier ist ein vereinfachtes Beispiel:
class Bakery(models.Model):
town = models.CharField(max_length=255)
class Cake(models.Model):
bakery = models.ForeignKey(Bakery, on_delete=models.CASCADE)
baked_at = models.DateTimeField()
Ich suche also nach einer Abfrage, die den neuesten Kuchen zurückgibt, der in jeder Bäckerei in Anytown, USA, gebacken wurde.
Antworten:
Soweit ich weiß, gibt es in Django ORM keinen einstufigen Weg, dies zu tun.
Sie können es jedoch in zwei Abfragen aufteilen:
bakeries = Bakery.objects.annotate( hottest_cake_baked_at=Max('cake__baked_at') ) hottest_cakes = Cake.objects.filter( baked_at__in=[b.hottest_cake_baked_at for b in bakeries] )
Wenn die IDs von Kuchen zusammen mit den Zeitstempeln von bake_at fortschreiten, können Sie den obigen Code vereinfachen und eindeutig definieren (falls zwei Kuchen gleichzeitig ankommen, können Sie beide erhalten):
hottest_cake_ids = Bakery.objects.annotate( hottest_cake_id=Max('cake__id') ).values_list('hottest_cake_id', flat=True) hottest_cakes = Cake.objects.filter(id__in=hottest_cake_ids)
Übrigens geht das an Daniel Roseman, der einmal eine ähnliche Frage von mir beantwortet hat:
http://groups.google.pl/group/django-users/browse_thread/thread/3b3cd4cbad478d34/3e4c87f336696054?hl=pl&q=
Wenn die obige Methode zu langsam ist, kenne ich auch die zweite Methode - Sie können benutzerdefiniertes SQL schreiben, das nur die Kuchen erzeugt, die in relevanten Bäckereien am heißesten sind, sie als Datenbankansicht definieren und dann ein nicht verwaltetes Django-Modell dafür schreiben. Es wird auch im obigen Thread für Django-Benutzer erwähnt. Der direkte Link zum ursprünglichen Konzept ist hier:
http://web.archive.org/web/20130203180037/http://wolfram.kriesing.de/blog/index.php/2007/django-nice-and-critical-article#comment-48425
Hoffe das hilft.
quelle
Django 1.11
jetzt gibt es einen Einwegschritt. Überprüfen Sie meine Antwort.Ausgehend von
Django 1.11
und dank Subquery und OuterRef können wir endlich einelatest-per-group
Abfrage mit dem erstellenORM
.hottest_cakes = Cake.objects.filter( baked_at=Subquery( (Cake.objects .filter(bakery=OuterRef('bakery')) .values('bakery') .annotate(last_bake=Max('baked_at')) .values('last_bake')[:1] ) ) ) #BONUS, we can now use this for prefetch_related() bakeries = Bakery.objects.all().prefetch_related( Prefetch('cake_set', queryset=hottest_cakes, to_attr='hottest_cakes' ) ) #usage for bakery in bakeries: print 'Bakery %s has %s hottest_cakes' % (bakery, len(bakery.hottest_cakes))
quelle
Wenn Sie zufällig PostGreSQL verwenden, können Sie die Django-Oberfläche verwenden, um EINZELTEILE ZU UNTERSCHEIDEN :
recent_cakes = Cake.objects.order_by('bakery__id', '-baked_at').distinct('bakery__id')
Wie in den Dokumenten angegeben , müssen Sie
order by
dieselben Felder wie Sie verwendendistinct on
. Wie Simon weiter unten ausgeführt hat, müssen Sie zusätzliche Sortierungen im Python-Raum vornehmen.quelle
Max
Annotationen ausprobiert und nach ihnen gefiltert, aber sie scheiterten schließlich auf der DB-Seite, weil SQL nicht korrekt war, nachdem der Django-Optimierer order_by entfernt hatte (bei Verwendung des Ergebnisses als Filter-Unterabfrage oder beim Aggregieren, z.count()
. B. ). Diese Lösung bricht nicht alle Dinge beim Abrufenrecent_cakes.count()
und wirft dabei keine FehlerCake.objects.filter(pk__in=recent_cackes).filter(other_conditions)
, aber das neueste Beispiel gibt zufällige Kuchen pro Bäckerei zurück, die andere Bedingungen erfüllen (nicht die heißesten!), Da Djangoorder_by
aus der Unterabfrage entfernt :(Dies sollte den Job machen:
from django.db.models import Max Bakery.objects.annotate(Max('cake__baked_at'))
quelle
Ich kämpfte mit einem ähnlichen Problem und kam schließlich zu einer folgenden Lösung. Es ist nicht abhängig von
order_by
unddistinct
kann daher auf der Datenbankseite wie gewünscht sortiert und auch als verschachtelte Abfrage zum Filtern verwendet werden. Ich glaube auch, dass diese Implementierung unabhängig von der DB-Engine ist, da sie auf der Standard-SQL-HAVING
Klausel basiert . Der einzige Nachteil ist, dass mehrere heißeste Kuchen pro Bäckerei zurückgegeben werden, wenn sie genau zur gleichen Zeit in dieser Bäckerei gebacken werden.from django.db.models import Max, F Cake.objects.annotate( # annotate with MAX "baked_at" over all cakes in bakery latest_baketime_in_bakery=Max('bakery__cake_set__baked_at') # compare this cake "baked_at" with annotated latest in bakery ).filter(latest_baketime_in_bakery__eq=F('baked_at'))
quelle
Cake.objects.filter(bakery__town="Anytown").order_by("-created_at")[:1]
Ich habe die Modelle an meinem Ende nicht ausgebaut, aber theoretisch sollte dies funktionieren. Heruntergebrochen:
Cake.objects.filter(bakery__town="Anytown")
Sollte alle Kuchen zurückgeben, die zu "Anytown" gehören, vorausgesetzt, das Land ist nicht Teil der Zeichenfolge. Das Doppelte unterstreicht zwischenbakery
undtown
ermöglicht uns den Zugriff auf dastown
Eigentum vonbakery
..order_by("-created_at")
Die Ergebnisse werden nach dem Erstellungsdatum sortiert, dem letzten zuerst (beachten Sie das-
(Minus-) Anmelden"-created_at"
. Ohne das Minuszeichen werden sie nach dem ältesten bis zum neuesten sortiert.[:1]
Am Ende wird nur das erste Element in der Liste zurückgegeben, das zurückgegeben wird (dies wäre eine Liste der Kuchen aus Anytown, sortiert nach dem letzten zuerst).Hinweis: Diese Antwort gilt für Django 1.11. Diese Antwort wurde gegenüber den hier in Django 1.11 Docs gezeigten Abfragen geändert .
quelle
@Tomasz Zieliński Lösung oben hat Ihr Problem gelöst, aber es hat meins nicht gelöst, weil ich den Kuchen noch filtern muss. Hier ist meine Lösung
from django.db.models import Q, Max hottest_yellow_round_cake = Max('cake__baked_at', filter=Q(cake__color='yellow', cake__shape='round')) bakeries = Bakery.objects.filter(town='Chicago').annotate( hottest_cake_baked_at=hottest_yellow_round_cake ) hottest_cakes = Cake.objects.filter( baked_at__in=[b.hottest_cake_baked_at for b in bakeries] )
Mit diesem Ansatz können Sie auch andere Dinge wie Filter, Bestellung, Paginierung für Kuchen implementieren
quelle