Rechercher un jeu de requêtes vide dans Django

183

Quel est l'idiome recommandé pour vérifier si une requête a renvoyé des résultats?
Exemple:

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

Je suppose qu'il existe plusieurs façons différentes de vérifier cela, mais j'aimerais savoir comment un utilisateur Django expérimenté le ferait. La plupart des exemples dans la documentation ignorent simplement le cas où rien n'a été trouvé ...

Niklas
la source

Réponses:

206
if not orgs:
    # Do this...
else:
    # Do that...
Adam
la source
5
Cela semble également être préféré dans la documentation, par exemple: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower
1
@Wtower Le code auquel vous faites référence a pour contrat de lever 404 si l'expression de filtrage n'atteint aucun enregistrement ou de produire un listrésultat s'il existe des enregistrements. Le code ne frappera la base de données qu'une seule fois. S'ils utilisaient exist()ou count()pour vérifier d'abord s'il y aura des enregistrements retournés, ils frapperaient la base de données deux fois (une fois pour vérifier, une fois pour obtenir les enregistrements). C'est une situation particulière. Cela n'implique pas que dans le cas général , la méthode préférée pour savoir si une requête renverra des enregistrements est d'utiliser doif queryset:...
Louis
1
@Louis le code auquel je fais référence n'est qu'un exemple qu'il contient une ligne if not my_objects:pour démontrer que c'est ainsi qu'ils le font dans la documentation. Tout le reste est totalement hors de propos, je ne comprends donc pas votre point de vue. Ils pourraient tout aussi bien faire mille requêtes et ce serait encore totalement hors de propos car ce n'est pas le but de cette réponse, avec laquelle je précise que je suis d'accord.
Wtower
1
@Wtower C'est juste une explication de la façon dont get_object_or_404fonctionne, pas une manière préférée de vérifier si des éléments existent dans un jeu de requêtes. Faire list () sur un ensemble de requêtes récupérera tous les objets d'un ensemble de requêtes, ce qui serait pire que d'interroger deux fois s'il y a beaucoup de lignes renvoyées.
minmaxavg
1
Pour une réponse plus détaillée, regardez la réponse de @ leonid-shvechikov ci-dessous: l'utilisation .exists()est plus efficace si le qs ne va pas être évalué.
guival
191

Depuis la version 1.2, Django a QuerySet. existe () méthode qui est la plus efficace:

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

Mais si vous envisagez d'évaluer QuerySet de toute façon, il est préférable d'utiliser:

if orgs:
   ...

Pour plus d'informations, lisez la documentation QuerySet.exists () .

Léonid Shvechikov
la source
.exists () est uniquement pour .filter (), y a-t-il quelque chose pour .get ()?
rouleau
.getne renvoie pas de jeu de requête. Il renvoie un objet. Alors google pour ça
Aseem
Ce n'est que nettement plus efficace si vous avez un grand QuerySet: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones
16

Si vous avez un grand nombre d'objets, cela peut (parfois) être beaucoup plus rapide:

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

Sur un projet sur lequel je travaille avec une énorme base de données, not orgsc'est plus de 400 ms et orgs.count()250 ms . Dans mes cas d'utilisation les plus courants (ceux où il y a des résultats), cette technique ramène souvent cela à moins de 20 ms. (Un cas que j'ai trouvé, c'était 6.)

Cela pourrait être beaucoup plus long, bien sûr, en fonction de la distance à parcourir dans la base de données pour trouver un résultat. Ou encore plus vite, s'il en trouve un rapidement; YMMV.

EDIT: Cela sera souvent plus lent que orgs.count()si le résultat n'est pas trouvé, en particulier si la condition sur laquelle vous filtrez est rare; par conséquent, il est particulièrement utile dans les fonctions de vue où vous devez vous assurer que la vue existe ou lancer Http404. (Là où, on pourrait l'espérer, les gens demandent des URL qui existent le plus souvent.)

Adam Playford
la source
10

Pour vérifier la vacuité d'un jeu de requêtes:

if orgs.exists():
    # Do something

ou vous pouvez rechercher le premier élément d'un jeu de requêtes, s'il n'existe pas, il retournera None:

if orgs.first():
    # Do something
Tuss4
la source
7
if orgs.exists()a fait l'objet d'une réponse apportée environ 5 ans avant celle-ci. La seule chose que cette réponse apporte à la table qui est peut - être nouvelle est if orgs.first(). (Même cela est discutable: est-ce substantiellement différent de faire ce qui a été orgs[0] suggéré il y a environ 5 ans?) Vous devriez développer cette partie de la réponse: quand voudrait-on faire cela au lieu des autres solutions proposées plus tôt?
Louis
9

Le moyen le plus efficace (avant django 1.2) est le suivant:

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...
Bartosz
la source
5
.exists () semble être encore plus efficace
dzida
5
Sauf que .exists () a été ajouté quelques mois après mon commentaire, et Django 1.2 (qui incorporait cette API) est sorti environ 8 mois plus tard. Mais merci pour votre vote négatif et ne pas avoir pris la peine de vérifier les faits.
Bartosz
4
Désolé, j'ai ajouté une petite modification à votre réponse pour la rendre plus précise et j'ai voté positivement.
dzida
4

Je ne suis pas d'accord avec le prédicat

if not orgs:

Ça devrait être

if not orgs.count():

J'avais le même problème avec un ensemble de résultats assez volumineux (~ 150k résultats). L'opérateur n'est pas surchargé dans QuerySet, donc le résultat est en fait décompressé sous forme de liste avant que la vérification ne soit effectuée. Dans mon cas, le temps d'exécution a diminué de trois ordres.

hedleyroos
la source
6
__nonzero__ est déjà surchargé dans QuerySet. Si le résultat n'est pas mis en cache (il ne l'est jamais lors de la première utilisation du jeu de requêtes), le comportement de __nonzero__ est d'itérer sur tous les éléments du jeu de requêtes. C'est très mauvais si l'ensemble est grand.
hedleyroos
0

Vous pouvez également utiliser ceci:

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

Rupesh Chaudhari
la source