Prowadziłem debatę na ten temat z kilkoma kolegami. Czy istnieje preferowany sposób pobierania obiektu w Django, gdy spodziewasz się tylko jednego?
Dwa oczywiste sposoby to:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
I:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
Pierwsza metoda wydaje się bardziej poprawna behawioralnie, ale używa wyjątków w przepływie sterowania, co może powodować pewne obciążenie. Drugi jest bardziej okrężny, ale nigdy nie spowoduje wyjątku.
Jakieś przemyślenia, które z nich są lepsze? Który jest bardziej wydajny?
QS.get()
jest dobry. 2. Szczegóły mają znaczenie: czy „oczekiwanie tylko jednego” oznacza zawsze 0-1 obiektów, czy też można mieć 2+ obiektów i ten przypadek też powinien być załatwiony (w tym przypadkulen(objs)
jest to okropny pomysł)? 3. Nie zakładaj niczego na temat narzutów bez benchmarku (myślę, że w tym przypadkutry/except
będzie szybciej, o ile przynajmniej połowa połączeń coś zwróci)Możesz zainstalować moduł o nazwie django-denerwujący, a następnie zrobić to:
źródło
1 jest poprawne. W Pythonie wyjątek ma taki sam narzut w stosunku do zwrotu. Aby uzyskać uproszczony dowód, możesz spojrzeć na to .
2 To właśnie robi Django na zapleczu.
get
wywołujefilter
i zgłasza wyjątek, jeśli nie zostanie znaleziony żaden element lub jeśli zostanie znalezionych więcej niż jeden obiekt.źródło
try
.Trochę spóźniłem się na imprezę, ale w Django 1.6 jest
first()
metoda na querysets.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Przykład:
źródło
Nie mogę rozmawiać z żadnym doświadczeniem Django, ale opcja nr 1 jasno mówi systemowi, że prosisz o 1 obiekt, podczas gdy druga opcja nie. Oznacza to, że opcja nr 1 może łatwiej skorzystać z indeksów pamięci podręcznej lub baz danych, zwłaszcza gdy atrybut, według którego filtrujesz, nie jest gwarantowany jako unikalny.
Również (ponownie spekulując) druga opcja może wymagać utworzenia jakiegoś rodzaju kolekcji wyników lub obiektu iteratora, ponieważ wywołanie filter () mogłoby normalnie zwrócić wiele wierszy. Pominąłbyś to za pomocą get ().
Wreszcie pierwsza opcja jest krótsza i pomija dodatkową zmienną tymczasową - tylko niewielka różnica, ale każda drobna różnica pomaga.
źródło
Dlaczego to wszystko działa? Zamień 4 linie na 1 wbudowany skrót. (To robi swoje własne próby / z wyjątkiem.)
źródło
Model.objects.get_or_create()
jestWięcej informacji o wyjątkach. Jeśli nie zostaną podniesione, prawie nic nie kosztują. Jeśli więc wiesz, że prawdopodobnie uzyskasz wynik, użyj wyjątku, ponieważ używając wyrażenia warunkowego płacisz koszty sprawdzenia za każdym razem, bez względu na wszystko. Z drugiej strony kosztują nieco więcej niż wyrażenie warunkowe, gdy są podniesione, więc jeśli spodziewasz się, że nie będą miały wyniku z pewną częstotliwością (powiedzmy, 30% czasu, jeśli pamięć służy), sprawdzenie warunkowe okaże się być trochę tańszym.
Ale to jest ORM Django i prawdopodobnie podróż w obie strony do bazy danych, a nawet wynik w pamięci podręcznej, prawdopodobnie zdominuje charakterystykę wydajności, więc w tym przypadku preferuj czytelność, ponieważ oczekujesz dokładnie jednego wyniku, użyj
get()
.źródło
Bawiłem się trochę tym problemem i odkryłem, że opcja 2 wykonuje dwa zapytania SQL, co dla tak prostego zadania jest zbyteczne. Zobacz moją adnotację:
Równoważna wersja, która wykonuje pojedyncze zapytanie to:
Przełączając się na to podejście, byłem w stanie znacznie zmniejszyć liczbę zapytań wykonywanych przez moją aplikację.
źródło
Ciekawe pytanie, ale dla mnie opcja nr 2 cuchnie przedwczesną optymalizacją. Nie jestem pewien, który jest bardziej wydajny, ale opcja nr 1 z pewnością wygląda i wydaje mi się bardziej pytoniczna.
źródło
Proponuję inny projekt.
Jeśli chcesz wykonać funkcję na możliwym wyniku, możesz wyprowadzić z QuerySet, na przykład: http://djangosnippets.org/snippets/734/
Wynik jest niesamowity, możesz na przykład:
W tym przypadku filtr zwraca pusty zestaw zapytań lub zestaw zapytań z pojedynczym elementem. Twoje niestandardowe funkcje zestawu zapytań również można łączyć w łańcuchy i używać ponownie. Jeśli chcesz wykonać to dla wszystkich wpisów:
MyModel.objects.all().yourFunction()
.Są również idealne do wykorzystania jako akcje w interfejsie administratora:
źródło
Opcja 1 jest bardziej elegancka, ale pamiętaj, aby użyć try..except.
Z własnego doświadczenia mogę ci powiedzieć, że czasami jesteś pewien, że nie może być więcej niż jeden pasujący obiekt w bazie danych, a mimo to będą dwa ... (z wyjątkiem oczywiście gdy otrzymujesz obiekt za pomocą klucza podstawowego).
źródło
Przepraszam, że dodam jeszcze jedno podejście do tego problemu, ale używam paginatora django, aw mojej aplikacji do administrowania danymi użytkownik może wybrać, czego dotyczy zapytania. Czasami jest to identyfikator dokumentu, ale poza tym jest to ogólne zapytanie zwracające więcej niż jeden obiekt, np. Queryset.
Jeśli użytkownik zapyta o identyfikator, mogę uruchomić:
co powoduje błąd w paginatorze django, ponieważ jest to Rekord, a nie Zestaw Zapytań Rekordów.
Muszę biec:
Która zwraca Queryset z jednym elementem. Wtedy paginator działa dobrze.
źródło
.dostać()
.filtr()
Uwaga
źródło