246 votes

Vérification de l'ensemble de requêtes vide dans Django

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')
# Si des résultats ont été renvoyés
    # Faites ceci avec les résultats sans refaire la requête.
# Sinon, faites autre chose...

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

285voto

Adam Points 2544
if not orgs:
    # Le Queryset est vide ...
else:
    # Le Queryset a des résultats ...

7 votes

Cela semble également préféré dans la documentation, par exemple : docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7

1 votes

@Wtower Le code auquel vous faites référence a pour contrat de renvoyer une erreur 404 si l'expression de filtrage ne trouve aucun enregistrement ou de produire une liste des résultats s'il y a des enregistrements. Le code là-bas interrogera la base de données qu'une seule fois. S'ils utilisaient exist() ou count() pour vérifier d'abord s'il y aurait des enregistrements renvoyés, ils interrogeraient la base de données deux fois (une fois pour vérifier, une fois pour obtenir les enregistrements). Il s'agit d'une situation spécifique. Cela ne signifie 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 de faire if queryset:...

1 votes

@Louis le code auquel je fais référence est uniquement un exemple qui 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 sujet donc je ne comprends pas ton point. Ils pourraient aussi bien faire mille requêtes et ce serait toujours totalement hors sujet car ce n'est pas le but de cette réponse, avec laquelle je précise que je suis d'accord.

244voto

Leonid Shvechikov Points 1524

Depuis la version 1.2, Django dispose de la méthode QuerySet.exists() qui est la plus efficace :

if orgs.exists():
    # Faire ceci...
else:
    # Faire cela...

Mais si vous allez évaluer QuerySet de toute façon, il vaut mieux utiliser :

if orgs:
   ...

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

1 votes

.exists() n'est utilisé que pour .filter(), y a-t-il quelque chose pour .get() ?

0 votes

.get ne renvoie pas un queryset. Il renvoie un objet. Donc recherchez sur google pour ça.

1 votes

Il est seulement sensiblement plus efficace si vous avez un grand QuerySet : docs.djangoproject.com/fr/2.1/ref/models/querysets/#exists

18voto

Adam Playford Points 127

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

try:
    orgs[0]
    # Si vous arrivez ici, c'est qu'il existe...
except IndexError:
    # N'existe pas !

Sur un projet sur lequel je travaille avec une énorme base de données, not orgs prend 400+ ms et orgs.count() prend 250ms. Dans la plupart des cas d'utilisation les plus courants (ceux où il y a des résultats), cette technique permet souvent de descendre en dessous de 20ms. (Un cas que j'ai trouvé était de 6.)

Cela pourrait prendre beaucoup plus de temps, bien sûr, en fonction de la distance que la base de données doit parcourir pour trouver un résultat. Ou même plus rapidement, s'il en trouve un rapidement ; cela dépendra de votre expérience.

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, c'est particulièrement utile dans les fonctions de vue où vous devez vous assurer que la vue existe ou renvoyer une Http404. (où, on espère, les gens demandent des URL qui existent plus souvent que non.)

12voto

Bartosz Points 2183

La manière la plus efficace (avant django 1.2) est la suivante:

if orgs.count() == 0:
    # aucun résultat
else:
    # très bien! continuons...

5 votes

.exists() semble être encore plus efficace

7 votes

Sauf que .exists() a été ajouté quelques mois après mon commentaire, et Django 1.2 (qui a intégré cette API) a été publié environ ~8 mois plus tard. Mais merci d'avoir voté négativement sans prendre la peine de vérifier les faits.

5 votes

Désolé, j'ai apporté une petite modification à votre réponse pour la rendre plus précise et j'ai voté positivement.

6voto

hedleyroos Points 57

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

if not orgs:

Il 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éballé comme une liste avant que la vérification ne soit effectuée. Dans mon cas, le temps d'exécution a diminué de trois ordres de grandeur.

6 votes

__nonzero__ est déjà surchargé dans QuerySet. Si le résultat n'est pas mis en cache (ce n'est jamais le cas lors de la première utilisation du queryset), le comportement de __nonzero__ est d'itérer sur tous les éléments du queryset. C'est très mauvais si l'ensemble est grand.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X