Souvent, je souhaite récupérer le premier objet d'un jeu de requêtes dans Django, ou revenir None
s'il n'y en a pas. Il existe de nombreuses façons de faire cela qui fonctionnent toutes. Mais je me demande lequel est le plus performant.
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 inutile. Est-ce 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 nécessite souvent la création d'un objet d'exception, ce qui est très gourmand en mémoire lorsque tout ce dont vous avez vraiment besoin est un test if trivial.
Comment puis-je faire cela avec un seul appel à la base de données et sans faire de mémoire avec des objets d'exception?
len()
sur les ensembles de requêtes, utilisez toujours.count()
.first()
etlast()
: docs.djangoproject.com/en/dev/ref/models/querysets/#firstRéponses:
Utilisez les méthodes pratiques
.first()
et.last()
:Ils avalent tous les deux l'exception résultante et retournent
None
si le jeu de requêtes ne renvoie aucun objet.Ceux-ci ont été ajoutés dans Django 1.6, qui a été publié en novembre 2013 .
la source
first()
etlast()
appliquer uneORDER BY
clause sur une requête. Cela rendra les résultats déterministes mais ralentira très probablement la requête.Vous pouvez utiliser le découpage de tableau :
Entry.objects.all()[:1].get()
Qui peut être utilisé avec
.filter()
:Entry.objects.filter()[:1].get()
Vous ne voudriez pas d'abord le transformer en liste car cela forcerait un appel de base de données complet de tous les enregistrements. Faites simplement ce qui précède et cela ne tirera que le premier. Vous pouvez même l'utiliser
.order_by()
pour vous assurer d'obtenir le premier que vous voulez.Assurez-vous d'ajouter le,
.get()
sinon vous obtiendrez un QuerySet et non un objet.la source
Entry.objects.all()[0]
??r = list(qs[:1]) if r: return r[0] return None
la source
LIMIT 1
à la requête, et je ne sais pas si vous pouvez faire mieux que cela. Cependant, en interne,__nonzero__
inQuerySet
est implémentétry: iter(self).next() except StopIteration: return false...
pour ne pas échapper à l'exception.QuerySet.__nonzero__()
n'est jamais appelé car leQuerySet
est converti en alist
avant de vérifier la justesse. D'autres exceptions peuvent cependant se produire.StopIteration
exception.__iter__
pour obtenir un nouvel objet itérateur et appeler sanext
méthode jusqu'à ce qu'elleStopIteration
soit lancée. Donc définitivement il y aura une exception quelque part;)Maintenant, dans Django 1.9, vous avez une
first()
méthode pour les ensembles de requêtes.C'est un meilleur moyen que
.get()
ou[0]
parce qu'il ne lève pas d'exception si le jeu de requêtes est vide, vous n'avez donc pas besoin de vérifier en utilisantexists()
la source
Si vous prévoyez d'obtenir souvent le premier élément, vous pouvez étendre QuerySet dans cette direction:
class FirstQuerySet(models.query.QuerySet): def first(self): return self[0] class ManagerWithFirstQuery(models.Manager): def get_query_set(self): return FirstQuerySet(self.model)
Définissez le modèle comme ceci:
class MyModel(models.Model): objects = ManagerWithFirstQuery()
Et utilisez-le comme ceci:
first_object = MyModel.objects.filter(x=100).first()
la source
Cela pourrait également fonctionner:
def get_first_element(MyModel): my_query = MyModel.objects.all() return my_query[:1]
s'il est vide, renvoie une liste vide, sinon il renvoie le premier élément d'une liste.
la source
Ça peut être comme ça
obj = model.objects.filter(id=emp_id)[0]
ou
obj = model.objects.latest('id')
la source
Vous devriez utiliser les méthodes django, comme existe. Il est là pour que vous puissiez l'utiliser.
if qs.exists(): return qs[0] return None
la source