Sprawdzanie pustego zestawu zapytań w Django


183

Jaki jest zalecany idiom do sprawdzania, czy zapytanie zwróciło jakieś wyniki?
Przykład:

orgs = Organisation.objects.filter(name__iexact = 'Fjuk inc')
# If any results
    # Do this with the results without querying again.
# Else, do something else...

Przypuszczam, że istnieje kilka różnych sposobów sprawdzenia tego, ale chciałbym wiedzieć, jak zrobiłby to doświadczony użytkownik Django. Większość przykładów w dokumentach po prostu ignoruje przypadek, w którym nic nie znaleziono ...

Odpowiedzi:


205
if not orgs:
    # Do this...
else:
    # Do that...

5
Wydaje się, że jest to również preferowane w dokumentacji, na przykład: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower

1
@Wtower Kod, do którego się odwołujesz, ma na celu podniesienie umowy 404, jeśli wyrażenie filtrujące nie trafi w żadne rekordy lub wygenerowanie listwyniku, jeśli istnieją rekordy. Tam kod trafi do bazy danych tylko raz. Gdyby użyli exist()lub count()najpierw sprawdzili, czy rekordy zostaną zwrócone, uderzyliby dwukrotnie w bazę danych (raz, aby sprawdzić, raz, aby uzyskać rekordy). To jest szczególna sytuacja. Nie oznacza to, że w ogólnym przypadku preferowaną metodą sprawdzenia, czy zapytanie zwróci rekordy, jest użycie doif queryset:...
Louis

1
@Louis, do którego się odwołuję, to tylko przykład, że zawiera wiersz if not my_objects:pokazujący, że tak robią to w dokumentach. Wszystko inne jest całkowicie nieistotne, więc nie rozumiem, o co ci chodzi. Równie dobrze mogliby zadać tysiące zapytań i byłoby to zupełnie nieistotne, ponieważ nie o to chodzi w tej odpowiedzi, z którą wyjaśniam, że się zgadzam.
Wtower,

1
@Wtower To tylko wyjaśnienie, jak get_object_or_404działa, a nie preferowany sposób sprawdzania, czy w zestawie zapytań istnieją jakieś elementy. Wykonanie list () w zestawie zapytań spowoduje pobranie wszystkich obiektów w zestawie zapytań, co byłoby gorsze niż dwukrotne zapytanie, jeśli zwróci się wiele wierszy.
minmaxavg

1
Aby uzyskać bardziej szczegółową odpowiedź, spójrz na odpowiedź @ leonid-shvechikov poniżej: użycie .exists()jest bardziej wydajne, jeśli qs nie będzie poddawany ocenie.
Guival

191

Od wersji 1.2 Django ma QuerySet. istnieje () metoda, która jest najbardziej wydajna:

if orgs.exists():
    # Do this...
else:
    # Do that...

Ale jeśli mimo wszystko zamierzasz ocenić QuerySet, lepiej użyć:

if orgs:
   ...

Aby uzyskać więcej informacji, przeczytaj dokumentację QuerySet.exists () .


.exists () jest tylko dla .filter (), czy jest coś dla .get ()?
rzuć

.getnie zwraca zestawu zapytań. Zwraca obiekt. Więc google dla tego
Aseem

Jest zauważalnie bardziej wydajny, jeśli masz duży zestaw QuerySet: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones,

16

Jeśli masz ogromną liczbę obiektów, może to (czasami) być znacznie szybsze:

try:
    orgs[0]
    # If you get here, it exists...
except IndexError:
    # Doesn't exist!

Nad projektem pracuję z ogromną bazą danych, która not orgsma ponad 400 ms i 250 ms orgs.count(). W moich najczęstszych przypadkach użycia (tych, w których są wyniki), ta technika często sprowadza to do poniżej 20ms. (Znalazłem jeden przypadek, było 6).

Może to oczywiście trwać znacznie dłużej, w zależności od tego, jak daleko baza danych musi szukać, aby znaleźć wynik. Lub nawet szybciej, jeśli szybko ją znajdzie; YMMV.

EDYCJA: Często będzie to wolniejsze, niż orgs.count()jeśli wynik nie zostanie znaleziony, szczególnie jeśli warunek, na którym filtrujesz, jest rzadki; w rezultacie jest szczególnie przydatny w funkcjach widoku, w których musisz się upewnić, że widok istnieje lub wyrzucić Http404. (Można się spodziewać, że ludzie pytają o adresy URL, które istnieją częściej niż nie).


10

Aby sprawdzić pustość zestawu zapytań:

if orgs.exists():
    # Do something

lub możesz sprawdzić pierwszy element w zestawie zapytań, jeśli nie istnieje, zwróci None:

if orgs.first():
    # Do something

7
if orgs.exists()została objęta odpowiedzią udzieloną około 5 lat wcześniej. Jedyną rzeczą, jaką ta odpowiedź przynosi być może nowemu stołowi, jest if orgs.first(). (Nawet to jest dyskusyjne: czy zasadniczo różni się to od wykonania orgs[0] sugerowanego również około 5 lat temu?) Należy rozwinąć tę część odpowiedzi: kiedy ktoś chciałby to zrobić zamiast innych proponowanych wcześniej rozwiązań?
Louis,

9

Najbardziej wydajnym sposobem (przed django 1.2) jest:

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...

5
.exists () wydaje się być jeszcze bardziej wydajny
dzida

5
Tyle że .exists () został dodany kilka miesięcy po moim komentarzu, a Django 1.2 (który zawierał ten interfejs API) został wydany około 8 miesięcy później. Ale dzięki za głosowanie w dół i nie zawracanie sobie głowy sprawdzaniem faktów.
Bartosz

4
Przepraszam, dodałem drobną edycję do twojej odpowiedzi, aby była dokładniejsza i głosowałem pozytywnie.
dzida

4

Nie zgadzam się z orzeczeniem

if not orgs:

Powinno być

if not orgs.count():

Miałem ten sam problem z dość dużym zestawem wyników (~ 150 000 wyników). Operator nie jest przeciążony w QuerySet, więc wynik jest faktycznie rozpakowywany jako lista przed sprawdzeniem. W moim przypadku czas realizacji skrócił się o trzy zamówienia.


6
__nonzero__ jest już przeciążony w QuerySet. Jeśli wynik nie jest buforowany (nigdy nie jest przy pierwszym użyciu zestawu zapytań), zachowanie __nonzero__ polega na iteracji wszystkich elementów w zestawie zapytań. Jest to bardzo złe, jeśli zestaw jest duży.
hedleyroos

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.