La forma más rápida de obtener el primer objeto de un queryset en django?


A menudo me encuentro queriendo obtener el primer objeto de un queryset en Django, o devolver None si no hay ninguno. Hay un montón de maneras de hacer esto que todos funcionan. Pero me pregunto cuál es la más performante.

qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
    return qs[0]
else:
    return None

¿Esto resulta en dos llamadas a la base de datos? Eso parece un desperdicio. ¿Esto es más rápido?

qs = MyModel.objects.filter(blah = blah)
if len(qs) > 0:
    return qs[0]
else:
    return None

Otra opción sería:

qs = MyModel.objects.filter(blah = blah)
try:
    return qs[0]
except IndexError:
    return None

Esto genera una sola llamada a la base de datos, lo cual es bueno. Pero requiere crear un objeto de excepción muchos de los tiempo, que es una cosa que consume mucha memoria cuando todo lo que realmente necesitas es una trivial prueba if.

¿Cómo puedo hacer esto con una sola llamada a la base de datos y sin agitar la memoria con objetos de excepción?

Author: Leopd, 2011-02-26

8 answers

Django 1.6 (publicado en noviembre de 2013) introdujo los métodos de conveniencia first() y last() que tragan la excepción resultante y devuelven None si el queryset no devuelve ningún objeto.

 269
Author: cod3monk3y,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-18 14:07:42

La respuesta correcta es

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

Que se puede utilizar en:

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

No querrás convertirlo primero en una lista porque eso forzaría una llamada completa a la base de datos de todos los registros. Simplemente haga lo anterior y solo tirará de la primera. Incluso podría usar .order_by para asegurarse de obtener el primero que desee.

Asegúrese de agregar el .get() o de lo contrario obtendrá un QuerySet y no un objeto.

 130
Author: stormlifter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-31 22:35:19
r = list(qs[:1])
if r:
  return r[0]
return None
 49
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-25 23:30:36

Ahora, en Django 1.9 tienes first() método para querysets.

YourModel.objects.all().first()

Esta es una mejor manera que .get() o [0] porque no arroja una excepción si queryset está vacío, por lo tanto, no necesita verificar usando exists()

 23
Author: levi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-06 02:01:57

Si planea obtener el primer elemento a menudo, puede extender QuerySet en esta dirección:

class FirstQuerySet(models.query.QuerySet):
    def first(self):
        return self[0]


class ManagerWithFirstQuery(models.Manager):
    def get_query_set(self):
        return FirstQuerySet(self.model)

Defina el modelo así:

class MyModel(models.Model):
    objects = ManagerWithFirstQuery()

Y úsalo así:

 first_object = MyModel.objects.filter(x=100).first()
 7
Author: Nikolay Fominyh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-13 12:24:01

Puede ser así

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

O

obj = model.objects.latest('id')
 4
Author: Nauman Tariq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-25 21:07:41

Debe usar métodos django, como exists. Está ahí para que lo uses.

if qs.exists():
    return qs[0]
return None
 3
Author: Ari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-13 22:23:33

Esto también podría funcionar:

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

Si está vacío, devuelve una lista vacía, de lo contrario devuelve el primer elemento dentro de una lista.

 2
Author: Nick Cuevas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-05 14:06:32