163 votes

Django filter versus get pour un seul objet ?

J'ai eu un débat à ce sujet avec quelques collègues. Existe-t-il un moyen privilégié de récupérer un objet dans Django lorsque l'on n'en attend qu'un seul ?

Les deux moyens les plus évidents sont :

   try: 
      obj = MyModel.objects.get(id=1)
   except MyModel.DoesNotExist:
      # we have no object!  do something
      pass

et

   objs = MyModel.objects.filter(id=1)
   if len(objs) == 1:
      obj = objs[0]
   else: 
      # we have no object!  do something
      pass

La première méthode semble plus correcte d'un point de vue comportemental, mais utilise des exceptions dans le flux de contrôle, ce qui peut introduire une certaine surcharge. La seconde est plus détournée mais ne lève jamais d'exception.

Avez-vous une idée de ce qui est préférable ? Laquelle est la plus efficace ?

8voto

Kylotan Points 14114

Je ne peux pas parler avec une quelconque expérience de Django mais l'option #1 indique clairement au système que vous demandez 1 objet, alors que la seconde option ne le fait pas. Cela signifie que l'option 1 pourrait plus facilement tirer parti des index de cache ou de base de données, en particulier lorsque l'attribut sur lequel vous filtrez n'est pas garanti comme unique.

De plus (encore une fois, je ne fais que spéculer), la deuxième option peut nécessiter la création d'une sorte de collection de résultats ou d'un objet itérateur puisque l'appel à la fonction filter() peut normalement renvoyer de nombreuses lignes. Vous pouvez contourner cela avec get().

Enfin, la première option est à la fois plus courte et supprime la variable temporaire supplémentaire - ce n'est qu'une différence mineure, mais chaque petite chose compte.

7voto

David Points 393

Pourquoi diable voudrais-je envelopper chaque .get() dans un try/except ? Pourquoi la méthode get() du gestionnaire n'adopterait-elle pas un comportement similaire à celui de la méthode get() du dict intégré ? Je soutiens que ce n'est pas Python que d'obliger le programmeur à envelopper un idiome aussi commun dans un try/except à chaque fois. En l'état actuel des choses, j'ai passé 20 minutes à essayer de trouver la bonne façon de faire un get parce que je ne pouvais pas croire que le try/except devrait être géré manuellement à chaque fois par conception.

7voto

IfLoop Points 59461

Quelques informations supplémentaires sur les exceptions. Si elles ne sont pas levées, elles ne coûtent presque rien. Ainsi, si vous savez que vous allez probablement avoir un résultat, utilisez l'exception, car en utilisant une expression conditionnelle vous payez le coût de la vérification à chaque fois, quoi qu'il arrive. D'autre part, elles coûtent un peu plus cher qu'une expression conditionnelle lorsqu'elles sont levées, donc si vous vous attendez à ne pas avoir de résultat avec une certaine fréquence (disons, 30% du temps, si ma mémoire est bonne), la vérification conditionnelle s'avère être un peu moins chère.

Mais il s'agit de l'ORM de Django, et il est probable que l'aller-retour vers la base de données, ou même un résultat mis en cache, domine les caractéristiques de performance, donc privilégiez la lisibilité, dans ce cas, puisque vous attendez exactement un résultat, utilisez get() .

4voto

Jan Wrobel Points 2290

J'ai joué un peu avec ce problème et j'ai découvert que l'option 2 exécute deux requêtes SQL, ce qui pour une tâche aussi simple est excessif. Voir mon annotation :

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

Une version équivalente qui exécute une seule requête est :

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

En adoptant cette approche, j'ai pu réduire considérablement le nombre de requêtes exécutées par mon application.

1voto

John McCollum Points 3270

Question intéressante, mais pour moi l'option 2 pue l'optimisation prématurée. Je ne suis pas sûr de ce qui est le plus performant, mais l'option 1 me semble certainement plus pythique.

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