A menudo me parece que quiero obtener el primer objeto de un queryset en Django, o devolver None
si no hay ninguno. Hay muchas formas de hacer esto que funcionan. Pero me pregunto cuál es el más eficiente.¿La forma más rápida de obtener el primer objeto de un conjunto de consulta en django?
qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
return qs[0]
else:
return None
¿Esto da como resultado 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 base de datos, lo cual es bueno. Pero requiere crear un objeto de excepción la mayor parte del tiempo, lo cual es una tarea muy intensiva en memoria cuando todo lo que realmente necesita es una prueba trivial.
¿Cómo puedo hacer esto con una sola llamada a la base de datos y sin agitar la memoria con objetos de excepción?
Regla de oro: si le preocupa minimizar los viajes de ida y vuelta de DB, no use 'len()' en los conjuntos de consulta, siempre use '.count()'. –
"crear un objeto de excepción muchas veces, que es una tarea muy intensiva en memoria": si le preocupa crear una excepción adicional, lo está haciendo mal ya que Python usa excepciones en todo el lugar. ¿Hizo una comparación real de que en su caso consume mucha memoria? – lqc
@Leopd Y si hubieras comparado el anwser de alguna manera (o al menos los comentarios), sabrías que no es más rápido. En realidad, puede ser más lento porque crea una lista adicional solo para desecharla. ¡Y todo eso es solo un cacahuete comparado con el costo de llamar a una función de pitón o usar el ORM de Django en primer lugar! Una sola llamada a filter() es muchas, muchas, * muchas * veces más lenta que generar una excepción (que aún se va a plantear, porque así es como funciona el protocolo de iterador). – lqc