Comprobación de conjunto de consultas vacío en Django

183

¿Cuál es el idioma recomendado para verificar si una consulta arrojó algún resultado?
Ejemplo:

orgs = Organisation.objects.filter(name__iexact = 'Fjuk inc')
# If any results
    # Do this with the results without querying again.
# Else, do something else...

Supongo que hay varias formas diferentes de verificar esto, pero me gustaría saber cómo lo haría un usuario experimentado de Django. La mayoría de los ejemplos en los documentos simplemente ignoran el caso donde no se encontró nada ...

Niklas
fuente

Respuestas:

206
if not orgs:
    # Do this...
else:
    # Do that...
Adán
fuente
55
Esto también parece preferirse en la documentación, por ejemplo: docs.djangoproject.com/en/1.8/topics/http/shortcuts/#id7
Wtower
1
@Wtower El código al que hace referencia tiene un contrato para elevar 404 si la expresión de filtrado no alcanza ningún registro o para producir un listresultado si hay registros. El código allí llegará a la base de datos solo una vez. Si solían exist()o count()para verificar primero si va a haber registros devueltos, estarían presionando la base de datos dos veces (una para verificar, una para obtener los registros). Esta es una situación específica. No implica que, en el caso general , el método preferido para saber si una consulta devolverá registros es usar doif queryset:...
Louis el
1
@Louis, el código al que me refiero es solo un ejemplo de que contiene una línea if not my_objects:para demostrar que así es como lo hacen en los documentos. Todo lo demás es completamente irrelevante, así que no entiendo tu punto. También podrían hacer miles de consultas y aún sería totalmente irrelevante ya que este no es el punto de esta respuesta, con lo que dejo en claro que estoy de acuerdo.
Wtower
1
@Wtower Esa es solo una explicación de cómo get_object_or_404funciona, no una forma preferida de verificar si existe algún elemento en un conjunto de consultas. Hacer list () en un conjunto de consultas buscará todos los objetos en un conjunto de consultas, lo que sería peor que consultar dos veces si se devuelven muchas filas.
minmaxavg
1
Para una respuesta más detallada, mire la respuesta de @leonid-shvechikov a continuación: el uso .exists()es más eficiente si no se va a evaluar el qs.
Guival
191

Desde la versión 1.2, Django tiene QuerySet. existe () método que es el más eficiente:

if orgs.exists():
    # Do this...
else:
    # Do that...

Pero si va a evaluar QuerySet de todos modos, es mejor usar:

if orgs:
   ...

Para obtener más información, lea la documentación de QuerySet.exists () .

Leonid Shvechikov
fuente
.exists () es solo para .filter (), ¿hay algo para .get ()?
roll
.getno devuelve un conjunto de consultas. Devuelve un objeto. Entonces google para eso
Aseem
Es notablemente más eficiente si tiene un QuerySet grande: docs.djangoproject.com/en/2.1/ref/models/querysets/#exists
Nathan Jones
16

Si tiene una gran cantidad de objetos, esto puede (a veces) ser mucho más rápido:

try:
    orgs[0]
    # If you get here, it exists...
except IndexError:
    # Doesn't exist!

En un proyecto en el que estoy trabajando con una gran base de datos, not orgstiene más de 400 ms y orgs.count()250 ms. En mis casos de uso más comunes (aquellos en los que hay resultados), esta técnica a menudo lo reduce a menos de 20 ms. (Un caso que encontré fue 6).

Podría ser mucho más largo, por supuesto, dependiendo de qué tan lejos tenga que buscar la base de datos para encontrar un resultado. O incluso más rápido, si encuentra uno rápidamente; YMMV.

EDITAR: Esto a menudo será más lento que orgs.count()si no se encuentra el resultado, particularmente si la condición en la que está filtrando es rara; Como resultado, es particularmente útil en las funciones de vista donde necesita asegurarse de que la vista existe o lanzar Http404. (Donde, uno esperaría, la gente está pidiendo URL que existen con mayor frecuencia).

Adam Playford
fuente
10

Para verificar el vacío de un conjunto de consultas:

if orgs.exists():
    # Do something

o puede buscar el primer elemento en un conjunto de consultas, si no existe devolverá None:

if orgs.first():
    # Do something
Tuss4
fuente
77
if orgs.exists()fue cubierto por una respuesta que se proporcionó unos 5 años antes de esta. Lo único que aporta esta respuesta a la tabla, que tal vez sea nuevo, es if orgs.first(). (Incluso esto es discutible: ¿es sustancialmente diferente de hacer lo orgs[0] sugerido hace aproximadamente 5 años también?) Debería desarrollar esa parte de la respuesta: ¿cuándo querría hacer esto en lugar de las otras soluciones propuestas anteriormente?
Louis
9

La forma más eficiente (antes de django 1.2) es esta:

if orgs.count() == 0:
    # no results
else:
    # alrigh! let's continue...
Bartosz
fuente
55
.exists () parece ser aún más eficiente
dzida
55
Excepto que .exists () se agregó unos meses después de mi comentario, y Django 1.2 (que incorporó esa API) se lanzó ~ 8 meses después. Pero gracias por rechazar y no molestarse en verificar los hechos.
Bartosz
44
Lo sentimos, agregué una pequeña edición a su respuesta para hacerlo más preciso y voté positivamente.
dzida
4

No estoy de acuerdo con el predicado

if not orgs:

Debería ser

if not orgs.count():

Estaba teniendo el mismo problema con un conjunto de resultados bastante grande (~ 150k resultados). El operador no está sobrecargado en QuerySet, por lo que el resultado en realidad se desempaqueta como una lista antes de realizar la verificación. En mi caso, el tiempo de ejecución se redujo en tres órdenes.

hedleyroos
fuente
66
__nonzero__ ya está sobrecargado en QuerySet. Si el resultado no se almacena en caché (nunca se usa por primera vez el conjunto de consultas), el comportamiento de __nonzero__ es iterar sobre todos los elementos del conjunto de consultas. Esto es muy malo si el conjunto es grande.
hedleyroos
0

También puedes usar esto:

if(not(orgs)): #if orgs is empty else: #if orgs is not empty

Rupesh Chaudhari
fuente