In Django doc,
select_related()
"folgt" Fremdschlüsselbeziehungen und wählt zusätzliche verwandte Objektdaten aus, wenn die Abfrage ausgeführt wird.
prefetch_related()
führt für jede Beziehung eine separate Suche durch und führt das "Beitreten" in Python durch.
Was bedeutet es, "in Python mitzumachen"? Kann jemand mit einem Beispiel illustrieren?
Mein Verständnis ist, dass für Fremdschlüsselbeziehung verwenden select_related
; und für die M2M-Beziehung verwenden prefetch_related
. Ist das richtig?
python
django
django-models
django-orm
NeoWang
quelle
quelle
Antworten:
Ihr Verständnis ist größtenteils richtig. Sie verwenden,
select_related
wenn das Objekt, das Sie auswählen möchten, ein einzelnes Objekt ist,OneToOneField
oder soForeignKey
. Sie verwenden,prefetch_related
wenn Sie eine "Menge" von Dingen erhalten möchten, alsoManyToManyField
s wie Sie angegeben haben oderForeignKey
s umkehren . Um zu verdeutlichen, was ich mit "reverseForeignKey
s" meine, hier ein Beispiel:Der Unterschied besteht darin, dass
select_related
ein SQL-Join ausgeführt wird und die Ergebnisse daher als Teil der Tabelle vom SQL-Server zurückgegeben werden.prefetch_related
führt andererseits eine andere Abfrage aus und reduziert daher die redundanten Spalten im ursprünglichen Objekt (ModelA
im obigen Beispiel). Sie könnenprefetch_related
für alles verwenden, für das Sie verwenden könnenselect_related
.Die Kompromisse sind, dass
prefetch_related
eine Liste von IDs erstellt und gesendet werden muss, um sie an den Server zurückzusenden. Dies kann eine Weile dauern. Ich bin mir nicht sicher, ob es eine gute Möglichkeit gibt, dies in einer Transaktion zu tun, aber ich verstehe, dass Django immer nur eine Liste sendet und SELECT sagt ... WHERE pk IN (..., ..., ...) Grundsätzlich. In diesem Fall kann dies sehr gut sein, wenn die vorab abgerufenen Daten spärlich sind (sagen wir US-Staatsobjekte, die mit den Adressen von Personen verknüpft sind). Wenn sie jedoch näher an Eins-zu-Eins liegen, kann dies viel Kommunikation verschwenden. Versuchen Sie im Zweifelsfall beide und sehen Sie, welche Leistung besser ist.Alles, was oben besprochen wurde, dreht sich im Wesentlichen um die Kommunikation mit der Datenbank. Auf der Python-Seite hat dies jedoch
prefetch_related
den zusätzlichen Vorteil, dass ein einzelnes Objekt verwendet wird, um jedes Objekt in der Datenbank darzustellen. Mitselect_related
doppelten Objekten werden in Python für jedes "übergeordnete" Objekt erstellt. Da Objekte in Python einen anständigen Speicheraufwand haben, kann dies ebenfalls eine Überlegung sein.quelle
select_related
ist eine Abfrage, währendprefetch_related
zwei ist, so dass die erstere schneller ist. Aberselect_related
ich werde dir nicht helfen fürManyToManyField
'sselect_related
Verwendet einen JOIN in SQL, währendprefetch_related
die Abfrage im ersten Modell ausgeführt wird, alle zum Vorabrufen erforderlichen IDs erfasst und anschließend eine Abfrage mit einer IN-Klausel in WHERE mit allen erforderlichen IDs ausgeführt wird. Wenn Sie sagen, 3-5 Modelle mit dem gleichen Fremdschlüssel,select_related
wird mit ziemlicher Sicherheit besser sein. Wenn Sie Hunderte oder Tausende von Modellen haben, die denselben Fremdschlüssel verwenden,prefetch_related
könnte dies tatsächlich besser sein. Zwischendurch müssen Sie testen und sehen, was passiert.Beide Methoden erreichen den gleichen Zweck, auf unnötige Datenbankabfragen zu verzichten. Sie verwenden jedoch unterschiedliche Ansätze für die Effizienz.
Der einzige Grund für die Verwendung einer dieser Methoden besteht darin, dass eine einzelne große Abfrage vielen kleinen Abfragen vorzuziehen ist. Django verwendet die große Abfrage, um präventiv Modelle im Speicher zu erstellen, anstatt bei Bedarf Abfragen für die Datenbank durchzuführen.
select_related
führt bei jeder Suche einen Join durch, erweitert jedoch die Auswahl um die Spalten aller verknüpften Tabellen. Dieser Ansatz hat jedoch eine Einschränkung.Joins können die Anzahl der Zeilen in einer Abfrage multiplizieren. Wenn Sie einen Join über einen Fremdschlüssel oder ein Eins-zu-Eins-Feld ausführen, wird die Anzahl der Zeilen nicht erhöht. Viele-zu-viele-Joins haben diese Garantie jedoch nicht. Daher beschränkt sich Django
select_related
auf Beziehungen, die nicht unerwartet zu einer massiven Verbindung führen.Das "Join in Python" für
prefetch_related
ist etwas alarmierender als es sein sollte. Es wird eine separate Abfrage für jede zu verbindende Tabelle erstellt. Es filtert jede dieser Tabellen mit einer WHERE IN-Klausel wie:Anstatt einen einzelnen Join mit möglicherweise zu vielen Zeilen durchzuführen, wird jede Tabelle in eine separate Abfrage aufgeteilt.
quelle
Wie die Django-Dokumentation sagt:
Weitere Informationen hierzu: https://docs.djangoproject.com/de/2.2/ref/models/querysets/#prefetch-related
quelle
Die bereits veröffentlichten Antworten sind durchgegangen. Ich dachte nur, es wäre besser, wenn ich eine Antwort mit einem tatsächlichen Beispiel hinzufügen würde.
Angenommen, Sie haben 3 verwandte Django-Modelle.
Hier können Sie das
M2
Modell und seine relativenM1
Objekte mithilfe vonselect_relation
Feld undM3
Objekte mithilfe vonprefetch_relation
Feld abfragen .Wie bereits erwähnt
M1
, gibt die Beziehung vonM2
aForeignKey
nur 1 Datensatz für einM2
Objekt zurück. Gleiches giltOneToOneField
auch.Aber
M3
die Beziehung vonM2
ist eine,ManyToManyField
die eine beliebige Anzahl vonM1
Objekten zurückgeben kann.Betrachten wir einen Fall , in dem Sie 2 haben
M2
Objektem21
,m22
die gleiche haben 5 zugehörigenM3
Objekte mit IDs1,2,3,4,5
. Wenn Sie zugeordneteM3
Objekte für jedes dieserM2
Objekte abrufen und select related verwenden, funktioniert dies folgendermaßen.Schritte:
m21
Objekt suchen .M3
Objekte ab, die sich auf einm21
Objekt beziehen, dessen IDs sind1,2,3,4,5
.m22
Objekt und alle anderenM2
Objekte.Wie wir gleiche haben
1,2,3,4,5
IDs für beidem21
,m22
Objekte, wenn wir Option select_related verwenden, ist es die DB zweimal für den gleichen IDs abzufragen geht , die bereits abgerufen wurden.Wenn Sie stattdessen prefetch_related verwenden und versuchen,
M2
Objekte abzurufen, werden alle IDs notiert, die Ihre Objekte zurückgegeben haben (Hinweis: nur die IDs), während Sie dieM2
Tabelle abfragen. Als letzten Schritt wird Django eine Abfrage an dieM3
Tabelle durchführen mit dem Satz aller IDs, die IhreM2
Objekte zurückgegeben haben. und verbinden Sie sie mitM2
Objekten mit Python anstelle der Datenbank.Auf diese Weise fragen Sie alle
M3
Objekte nur einmal ab, was die Leistung verbessert.quelle