J'avais un débat à ce sujet avec certains collègues. Existe-t-il un moyen préféré de récupérer un objet dans Django lorsque vous n'en attendez qu'un?
Les deux moyens é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 sur le plan du comportement, mais utilise des exceptions dans le flux de contrôle qui peuvent introduire une surcharge. Le second est plus rond-point mais ne soulèvera jamais d'exception.
Y a-t-il des réflexions sur laquelle de celles-ci est préférable? Qu'est-ce qui est le plus efficace?
QS.get()
c'est bien. 2. Les détails importent: est-ce que "n'attendre qu'un seul" signifie toujours 0-1 objets, ou il est possible d'avoir plus de 2 objets et ce cas devrait également être traité (dans ce caslen(objs)
est une idée terrible)? 3. Ne présumez rien des frais généraux sans benchmark (je pense que dans ce cas, cetry/except
sera plus rapide tant qu'au moins la moitié des appels retournent quelque chose)Vous pouvez installer un module appelé django-ennoying puis faire ceci:
la source
1 est correct. En Python, une exception a une surcharge égale à un retour. Pour une preuve simplifiée, vous pouvez regarder ceci .
2 C'est ce que fait Django dans le backend.
get
appellefilter
et lève une exception si aucun élément n'est trouvé ou si plus d'un objet est trouvé.la source
try
.Je suis un peu en retard à la fête, mais avec Django 1.6 il y a la
first()
méthode sur les ensembles de requêtes.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Exemple:
la source
Je ne peux parler d'aucune expérience de Django mais l'option n ° 1 indique clairement au système que vous demandez 1 objet, alors que la deuxième option ne le fait pas. Cela signifie que l'option n ° 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 d'être unique.
Aussi (encore une fois, en spéculant) la deuxième option peut avoir à créer une sorte de collection de résultats ou d'objet itérateur puisque l'appel de filter () pourrait normalement retourner de nombreuses lignes. Vous contourneriez cela avec get ().
Enfin, la première option est à la fois plus courte et omet la variable temporaire supplémentaire - seulement une différence mineure mais chaque petite aide.
la source
Pourquoi tout ça marche? Remplacez 4 lignes par 1 raccourci intégré. (Cela fait son propre essai / sauf.)
la source
Model.objects.get_or_create()
est pourQuelques informations supplémentaires sur les exceptions. S'ils ne sont pas élevés, ils 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'un autre côté, ils coûtent un peu plus cher qu'une expression conditionnelle lorsqu'ils sont augmentés, donc si vous vous attendez à ne pas avoir de résultat avec une certaine fréquence (par exemple, 30% du temps, si la mémoire est bonne), la vérification conditionnelle s'avère pour être un peu moins cher.
Mais c'est l'ORM de Django, et probablement l'aller-retour vers la base de données, ou même un résultat mis en cache, est susceptible de dominer les caractéristiques de performance, alors privilégiez la lisibilité, dans ce cas, puisque vous attendez exactement un résultat, utilisez
get()
.la source
J'ai un peu joué 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:
Une version équivalente qui exécute une seule requête est:
En passant à cette approche, j'ai pu réduire considérablement le nombre de requêtes exécutées par mon application.
la source
Question intéressante, mais pour moi, l'option n ° 2 pue une optimisation prématurée. Je ne suis pas sûr de ce qui est le plus performant, mais l'option n ° 1 me semble certainement plus pythonique.
la source
Je suggère un design différent.
Si vous souhaitez exécuter une fonction sur un résultat possible, vous pouvez dériver de QuerySet, comme ceci: http://djangosnippets.org/snippets/734/
Le résultat est plutôt génial, vous pourriez par exemple:
Ici, le filtre renvoie un jeu de requêtes vide ou un jeu de requêtes avec un seul élément. Vos fonctions de jeu de requêtes personnalisées sont également chaînables et réutilisables. Si vous souhaitez effectuer pour toutes vos entrées:
MyModel.objects.all().yourFunction()
.Ils sont également idéaux pour être utilisés comme actions dans l'interface d'administration:
la source
L'option 1 est plus élégante, mais assurez-vous d'utiliser try..except.
D'après ma propre expérience, je peux vous dire que parfois vous êtes sûr qu'il ne peut pas y avoir plus d'un objet correspondant dans la base de données, et pourtant il y en aura deux ... (sauf bien sûr lors de l'obtention de l'objet par sa clé primaire).
la source
Désolé d'ajouter un autre point sur ce problème, mais j'utilise le paginateur django, et dans mon application d'administration de données, l'utilisateur est autorisé à choisir ce sur quoi interroger. Parfois, c'est l'identifiant d'un document, mais sinon c'est une requête générale renvoyant plus d'un objet, c'est-à-dire un Queryset.
Si l'utilisateur interroge l'identifiant, je peux exécuter:
qui jette une erreur dans le paginateur de django, car il s'agit d'un Record et non d'un Queryset of Records.
J'ai besoin de courir:
Ce qui renvoie un Queryset contenant un élément. Ensuite, le paginateur fonctionne très bien.
la source
.avoir()
.filtre()
Remarque
la source