web-dev-qa-db-fra.com

Le moyen le plus rapide d’obtenir le premier objet d’un ensemble de requêtes dans Django?

Souvent, je cherche à obtenir le premier objet d'un ensemble de requêtes dans Django ou à renvoyer None s'il n'y en a pas. Il y a beaucoup de façons de faire qui fonctionnent. Mais je me demande quel 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 de base de données? Cela semble inutile. 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. Cependant, il est souvent nécessaire de créer un objet exception, ce qui est une opération très gourmande en mémoire lorsque tout ce dont vous avez besoin est un simple if-test.

Comment puis-je faire cela avec un seul appel de base de données et sans perdre de la mémoire avec des objets d'exception?

172
Leopd

Django 1.6 (publié en novembre 2013) introduit les méthodes pratiquesfirst() et last() qui avalent l'exception résultante et renvoient None si le queryset ne renvoie aucun objet.

300
cod3monk3y

La bonne réponse est

Entry.objects.all()[:1].get()

qui peut être utilisé dans:

Entry.objects.filter()[:1].get()

Vous ne voudriez pas d'abord le transformer en liste car cela obligerait un appel complet à la base de données de tous les enregistrements. Faites juste ce qui précède et il ne tirera que le premier. Vous pouvez même utiliser .order_by pour vous assurer d'obtenir le premier résultat souhaité.

Assurez-vous d’ajouter la .get() sinon vous obtiendrez un QuerySet et non un objet.

135
stormlifter
r = list(qs[:1])
if r:
  return r[0]
return None
49

Maintenant, dans Django 1.9, vous avez la méthode first() pour les ensembles de requêtes.

YourModel.objects.all().first()

C'est un meilleur moyen que .get() ou [0] car il ne lève pas d'exception si queryset est vide. Par conséquent, vous n'avez pas besoin de vérifier en utilisant exists()

30
levi

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()
7
Nikolay Fominyh

Ça peut être comme ça

obj = model.objects.filter(id=emp_id)[0]

ou

obj = model.objects.latest('id')
5
Nauman Tariq

Cela pourrait fonctionner aussi bien:

def get_first_element(MyModel):
    my_query = MyModel.objects.all()
    return my_query[:1]

s'il est vide, retourne une liste vide, sinon il retourne le premier élément de la liste.

4
Nick Cuevas

Vous devez utiliser les méthodes Django, comme il en existe. C'est là pour vous de l'utiliser.

if qs.exists():
    return qs[0]
return None
3
Ari

Depuis Django 1.6, vous pouvez utiliser filter () avec la méthode first () comme ceci:

Model.objects.filter(field_name=some_param).first()
0
dtar