Django: limitar los resultados de la consulta

200

Quiero tomar las últimas 10 instancias de un modelo y tener este código:

 Model.objects.all().order_by('-id')[:10]

¿Es cierto que primero recoge todas las instancias y luego toma solo 10 últimas? ¿Hay algún método más efectivo?

krzyhub
fuente

Respuestas:

304

Los conjuntos de consultas de Django son vagos. Eso significa que una consulta llegará a la base de datos solo cuando solicite específicamente el resultado.

Entonces, hasta que imprima o realmente use el resultado de una consulta, puede filtrar aún más sin acceso a la base de datos.

Como puede ver a continuación, su código solo ejecuta una consulta sql para obtener solo los últimos 10 elementos.

In [19]: import logging                                 
In [20]: l = logging.getLogger('django.db.backends')    
In [21]: l.setLevel(logging.DEBUG)                      
In [22]: l.addHandler(logging.StreamHandler())      
In [23]: User.objects.all().order_by('-id')[:10]          
(0.000) SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined" FROM "auth_user" ORDER BY "auth_user"."id" DESC LIMIT 10; args=()
Out[23]: [<User: hamdi>]
hamdiakoguz
fuente
Intenté esto en mongoDB y dice que SELECT no es compatible. ¿Cómo hacer esto en mongoDB?
winux
@winux Dado que esto es específico de Django, parece que es posible que deba considerar configurar Django para que funcione específicamente con bases de datos de tipo Mongo / NoSQL. Esa no es una configuración típica en mi experiencia, con respecto a la configuración estándar de Django ORM.
cobarde anónimo
38

En realidad, creo LIMIT 10que se emitirá a la base de datos, por lo que no se producirá el corte en Python sino en la base de datos.

Consulte la limitación de conjuntos de consultas para obtener más información.

Davor Lucic
fuente
Tenga en cuenta que esto no funcionará para los conjuntos de consultas que también necesitan filtrado, ya que no puede filtrar después de segmentar.
Mike 'Pomax' Kamermans
2
Así que filtre primero que cortarlo. Gracias Davor por el enlace!
Vyachez
13

Parece que la solución en la pregunta ya no funciona con Django 1.7 y genera un error: "No se puede reordenar una consulta una vez que se ha tomado un segmento"

De acuerdo con la documentación https://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets que obliga al parámetro "paso" de la sintaxis de corte Python evalúa la consulta. Funciona de esta manera:

Model.objects.all().order_by('-id')[:10:1]

Aún así, me pregunto si el límite se ejecuta en secciones de SQL o Python que devuelve toda la matriz de resultados. No es bueno recuperar grandes listas en la memoria de la aplicación.

Nikolay Grischenko
fuente
Incluso esta solución no funciona con django> = 1.8 probado.
sonus21
3

Si. Si desea obtener un subconjunto limitado de objetos, puede hacerlo con el siguiente código:

Ejemplo:

obj=emp.objects.all()[0:10]

El principio 0 es opcional, entonces

obj=emp.objects.all()[:10]

El código anterior devuelve las primeras 10 instancias.

patel shahrukh
fuente
1

Como una adición y observación a las otras respuestas útiles, vale la pena notar que, en realidad, hacer un [:10]corte devolverá los primeros 10 elementos de la lista , no los últimos 10 ...

Para obtener los últimos 10 debe hacer en su [-10:]lugar (ver aquí ). Esto le ayudará a evitar el uso order_by('-id')con el -de revertir los elementos.

DarkCygnus
fuente
1
Intenté esto y obtuve "La indexación negativa no es compatible".
bparker
@DarkCygnus Product.objects.filter(~Q(price=0))[-5:]me causa el mismo error: "No se admite la indexación negativa".
bersam
Esto no funciona en django en un conjunto de consultas: code.djangoproject.com/ticket/13089 Si convierte el conjunto de consultas a una lista, funcionará.
Valem