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.