Souvent, je me retrouve à vouloir récupérer le premier objet d'un queryset dans Django, ou retourner None
s'il n'y en a pas. Il existe de nombreuses façons de procéder qui fonctionnent toutes. Mais je me demande laquelle est la plus performante.
qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
return qs[0]
else:
return None
Cela entraîne-t-il deux appels à la base de données ? Cela semble être du gaspillage. Est-ce que c'est plus rapide ?
qs = MyModel.objects.filter(blah = blah)
if len(qs) > 0:
return qs[0]
else:
return None
Une autre option serait :
qs = MyModel.objects.filter(blah = blah)
try:
return qs[0]
except IndexError:
return None
Cela génère un seul appel à la base de données, ce qui est bien. Mais elle nécessite la création d'un objet d'exception la plupart du temps, ce qui est très gourmand en mémoire lorsque tout ce dont vous avez besoin est un test if trivial.
Comment puis-je faire cela avec un seul appel à la base de données et sans faire tourner la mémoire avec des objets d'exception ?
27 votes
Règle générale : Si vous êtes soucieux de minimiser les allers-retours de la DB, n'utilisez pas
len()
sur les querysets, utilisez toujours.count()
.7 votes
"Si vous êtes préoccupé par la création d'une exception supplémentaire, alors vous vous trompez, car Python utilise des exceptions partout. Est-ce que vous avez réellement évalué que c'est gourmand en mémoire dans votre cas ?
1 votes
@Leopd Et si vous aviez réellement évalué la réponse d'une manière ou d'une autre (ou au moins les commentaires), vous sauriez que ce n'est pas plus rapide. En fait, ça peut être plus lent, parce que vous créez une liste supplémentaire juste pour la jeter. Et tout cela n'est rien comparé au coût de l'appel d'une fonction python ou de l'utilisation de l'ORM de Django en premier lieu ! Un seul appel à filter() représente beaucoup, beaucoup de choses, beaucoup de fois plus lent que de lever une exception (qui sera quand même levée, car c'est comme ça que le protocole des itérateurs fonctionne !)
1 votes
Votre intuition est correcte, la différence de performance est faible, mais votre conclusion est fausse. J'ai effectué un benchmark et la réponse acceptée est en fait plus rapide d'une marge réelle. Allez comprendre.
11 votes
Pour les personnes utilisant Django 1.6, ils ont finalement ajouté la fonction
first()
etlast()
des méthodes pratiques : docs.djangoproject.com/fr/dev/ref/models/querysets/#first0 votes
@DanielDiPaolo - C'est une mauvaise règle empirique. Les développeurs doivent comprendre quand les QuerySets sont évalués. Si vous allez utiliser les enregistrements de toute façon, l'utilisation de la fonction
len
est préférable. Voir docs.djangoproject.com/fr/dev/topics/db/optimization/