Ich hatte mit einigen Kollegen eine Debatte darüber. Gibt es eine bevorzugte Möglichkeit, ein Objekt in Django abzurufen, wenn Sie nur eines erwarten?
Die zwei offensichtlichen Wege sind:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
Und:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
Die erste Methode scheint verhaltensmäßig korrekter zu sein, verwendet jedoch Ausnahmen im Kontrollfluss, die einen gewissen Overhead verursachen können. Der zweite ist mehr Kreisverkehr, wird aber nie eine Ausnahme auslösen.
Irgendwelche Gedanken darüber, welche davon vorzuziehen sind? Welches ist effizienter?
QS.get()
ist es gut. 2. Details sind wichtig: Bedeutet "nur eines erwarten" immer 0-1 Objekte, oder es ist möglich, 2+ Objekte zu haben, und dieser Fall sollte auch behandelt werden (in diesem Falllen(objs)
ist eine schreckliche Idee)? 3. Nehmen Sie nichts über Overhead ohne Benchmark an (ich denke, dass in diesem Falltry/except
schneller sein wird, solange mindestens die Hälfte der Anrufe etwasSie können ein Modul namens django-nervig installieren und dann Folgendes tun:
quelle
1 ist richtig. In Python hat eine Ausnahme den gleichen Aufwand wie eine Rückgabe. Für einen vereinfachten Beweis können Sie dies betrachten .
2 Dies ist, was Django im Backend tut.
get
ruftfilter
eine Ausnahme auf und löst sie aus, wenn kein Element gefunden wird oder wenn mehr als ein Objekt gefunden wird.quelle
try
.Ich bin etwas spät zur Party, aber mit Django 1.6 gibt es die
first()
Methode für Abfragesätze.https://docs.djangoproject.com/de/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Beispiel:
quelle
Ich kann mit keiner Erfahrung von Django sprechen, aber Option 1 sagt dem System deutlich, dass Sie nach 1 Objekt fragen, während die zweite Option dies nicht tut. Dies bedeutet, dass Option 1 die Vorteile von Cache- oder Datenbankindizes leichter nutzen kann, insbesondere wenn nicht garantiert ist, dass das Attribut, nach dem Sie filtern, eindeutig ist.
Außerdem muss (erneut spekulierend) die zweite Option möglicherweise eine Art Ergebnissammlung oder ein Iteratorobjekt erstellen, da der Aufruf von filter () normalerweise viele Zeilen zurückgeben kann. Sie würden dies mit get () umgehen.
Schließlich ist die erste Option kürzer und lässt die zusätzliche temporäre Variable weg - nur ein kleiner Unterschied, aber jedes bisschen hilft.
quelle
Warum funktioniert das alles? Ersetzen Sie 4 Zeilen durch 1 integrierte Verknüpfung. (Dies macht seinen eigenen Versuch / außer.)
quelle
Model.objects.get_or_create()
fürWeitere Informationen zu Ausnahmen. Wenn sie nicht aufgezogen werden, kosten sie fast nichts. Wenn Sie also wissen, dass Sie wahrscheinlich ein Ergebnis erzielen werden, verwenden Sie die Ausnahme, da Sie bei Verwendung eines bedingten Ausdrucks die Kosten für die Überprüfung jedes Mal bezahlen, egal was passiert. Auf der anderen Seite kosten sie etwas mehr als einen bedingten Ausdruck, wenn sie ausgelöst werden. Wenn Sie also erwarten, dass Sie nicht mit einer bestimmten Häufigkeit ein Ergebnis erzielen (z. B. 30% der Zeit, wenn Speicherplatz zur Verfügung steht), wird die bedingte Prüfung durchgeführt ein bisschen billiger sein.
Dies ist jedoch Djangos ORM, und wahrscheinlich dominiert der Roundtrip zur Datenbank oder sogar ein zwischengespeichertes Ergebnis die Leistungsmerkmale. Begünstigen Sie daher in diesem Fall die Lesbarkeit, da Sie genau ein Ergebnis erwarten, verwenden Sie
get()
.quelle
Ich habe ein bisschen mit diesem Problem gespielt und festgestellt, dass die Option 2 zwei SQL-Abfragen ausführt, was für eine so einfache Aufgabe übermäßig ist. Siehe meine Anmerkung:
Eine äquivalente Version, die eine einzelne Abfrage ausführt, ist:
Durch die Umstellung auf diesen Ansatz konnte ich die Anzahl der von meiner Anwendung ausgeführten Abfragen erheblich reduzieren.
quelle
Interessante Frage, aber für mich stinkt Option 2 nach vorzeitiger Optimierung. Ich bin mir nicht sicher, was performanter ist, aber Option 1 sieht für mich auf jeden Fall pythonischer aus und fühlt sich auch so an.
quelle
Ich schlage ein anderes Design vor.
Wenn Sie eine Funktion für ein mögliches Ergebnis ausführen möchten, können Sie QuerySet wie folgt ableiten: http://djangosnippets.org/snippets/734/
Das Ergebnis ist ziemlich beeindruckend, Sie könnten zum Beispiel:
Hier gibt der Filter entweder ein leeres Abfrageset oder ein Abfrageset mit einem einzelnen Element zurück. Ihre benutzerdefinierten Abfragesatzfunktionen sind auch verkettbar und wiederverwendbar. Wenn Sie es für alle Ihre Einträge ausführen möchten:
MyModel.objects.all().yourFunction()
.Sie eignen sich auch ideal als Aktionen in der Administrationsoberfläche:
quelle
Option 1 ist eleganter, aber verwenden Sie unbedingt try..except.
Aus eigener Erfahrung kann ich Ihnen sagen, dass Sie manchmal sicher sind, dass möglicherweise nicht mehr als ein übereinstimmendes Objekt in der Datenbank vorhanden sein kann, und dennoch zwei ... (außer natürlich, wenn Sie das Objekt anhand seines Primärschlüssels abrufen).
quelle
Es tut mir leid, dass ich noch eine Einstellung zu diesem Problem hinzugefügt habe, aber ich verwende den Django-Paginator. In meiner Datenadministrations-App kann der Benutzer auswählen, was abgefragt werden soll. Manchmal ist dies die ID eines Dokuments, ansonsten handelt es sich um eine allgemeine Abfrage, die mehr als ein Objekt zurückgibt, dh ein Abfragesatz.
Wenn der Benutzer die ID abfragt, kann ich Folgendes ausführen:
Dies wirft einen Fehler in Djangos Paginator auf, da es sich um einen Datensatz und nicht um eine Abfragemenge von Datensätzen handelt.
Ich muss rennen:
Was ein Queryset mit einem Element zurückgibt. Dann funktioniert der Paginator einwandfrei.
quelle
.bekommen()
.Filter()
Hinweis
quelle