Verifica della query vuota in Django


183

Qual è il linguaggio raccomandato per verificare se una query ha restituito risultati?
Esempio:

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

Suppongo che ci siano diversi modi per verificarlo, ma vorrei sapere come lo farebbe un utente Django esperto. La maggior parte degli esempi nei documenti ignora il caso in cui non è stato trovato nulla ...

Risposte:


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

5
Questo sembra essere preferito anche nella documentazione, ad esempio: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower,

1
@Wtower Il codice a cui si fa riferimento ha il contratto per aumentare 404 se l'espressione di filtraggio non raggiunge alcun record o per produrre un listrisultato se ci sono record. Il codice lì colpirà il database solo una volta. Se usassero exist()o count()per prima cosa controllassero se sarebbero stati restituiti dei record, avrebbero colpito il database due volte (una volta per controllare, una volta per ottenere i record). Questa è una situazione specifica. Ciò non implica che, nel caso generale , il metodo preferito per sapere se una query restituirà i record è utilizzare doif queryset:...
Louis

1
@ Il codice a cui mi riferisco è solo un esempio del fatto che contiene una riga if not my_objects:per dimostrare che è così che lo fanno nei documenti. Tutto il resto è assolutamente irrilevante, quindi non capisco il tuo punto. Potrebbero anche fare mille domande e sarebbe ancora del tutto irrilevante in quanto non è questo il punto di questa risposta, con la quale chiarisco che sono d'accordo.
Wtower,

1
@Wtower Questa è solo una spiegazione di come get_object_or_404funziona, non un modo preferito di verificare l'esistenza di elementi in un queryset. Fare list () su un queryset recupererà tutti gli oggetti su un queryset, il che sarebbe peggio che interrogare due volte se vengono restituite molte righe.
minmaxavg,

1
Per una risposta più dettagliata guarda la risposta di @ leonid-shvechikov di seguito: usare .exists()è più efficiente se non si intende valutare la qs.
Guiva

191

Dalla versione 1.2, Django ha QuerySet. esiste () metodo che è il più efficiente:

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

Ma se hai intenzione di valutare QuerySet è comunque meglio usare:

if orgs:
   ...

Per ulteriori informazioni, leggere la documentazione di QuerySet.exists () .


.exists () è solo per .filter (), c'è qualcosa per .get ()?
roll

.getnon restituisce un queryset. Restituisce un oggetto. Quindi google per quello
Aseem

È notevolmente più efficiente se hai un QuerySet di grandi dimensioni: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones,

16

Se hai un numero enorme di oggetti, questo può (a volte) essere molto più veloce:

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

Su un progetto a cui sto lavorando con un enorme database, not orgsè di 400+ ms e orgs.count()250ms. Nei miei casi d'uso più comuni (quelli in cui ci sono risultati), questa tecnica spesso lo porta a meno di 20 ms. (Un caso che ho trovato, era 6.)

Potrebbe essere molto più lungo, ovviamente, a seconda di quanto deve guardare il database per trovare un risultato. O ancora più veloce, se ne trova uno rapidamente; YMMV.

EDIT: questo sarà spesso più lento rispetto a orgs.count()quando il risultato non viene trovato, in particolare se la condizione su cui stai filtrando è rara; di conseguenza, è particolarmente utile nelle funzioni di visualizzazione in cui è necessario assicurarsi che la vista esista o lanciare Http404. (Dove, si spera, le persone chiedono URL che esistono più spesso).


10

Per verificare il vuoto di un queryset:

if orgs.exists():
    # Do something

oppure puoi cercare un primo elemento in un queryset, se non esiste restituirà None:

if orgs.first():
    # Do something

7
if orgs.exists()è stato coperto da una risposta che è stata fornita circa 5 anni prima di questa. L'unica cosa che questa risposta porta al tavolo che è forse nuova è if orgs.first(). (Anche questo è discutibile: è sostanzialmente diverso dal fare anche quello orgs[0] suggerito circa 5 anni fa?) Dovresti sviluppare quella parte della risposta: quando si vorrebbe fare questo invece delle altre soluzioni proposte prima?
Louis,

9

Il modo più efficiente (prima di Django 1.2) è questo:

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

5
.exists () sembra essere ancora più efficiente
dzida

5
Solo che .exists () è stato aggiunto pochi mesi dopo il mio commento e Django 1.2 (che incorporava quell'API) è stato rilasciato ~ 8 mesi dopo. Ma grazie per il voto negativo e non si preoccupa di controllare i fatti.
Bartosz,

4
Spiacenti, ho aggiunto una piccola modifica alla tua risposta per renderla più accurata e votata positivamente.
dzida,

4

Non sono d'accordo con il predicato

if not orgs:

Dovrebbe essere

if not orgs.count():

Stavo avendo lo stesso problema con un set di risultati abbastanza grande (~ 150k risultati). L'operatore non è sovraccaricato in QuerySet, quindi il risultato viene effettivamente decompresso come elenco prima dell'esecuzione del controllo. Nel mio caso i tempi di esecuzione sono diminuiti di tre ordini.


6
__nonzero__ è già sovraccarico in QuerySet. Se il risultato non viene memorizzato nella cache (non è mai al primo utilizzo del queryset) il comportamento di __nonzero__ è quello di iterare su tutti gli elementi nel queryset. Questo è molto brutto se il set è grande.
Hedleyroos,

0

Puoi anche usare questo:

if(not(orgs)): #if orgs is empty else: #if orgs is not empty

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.