Estaba teniendo un debate sobre esto con algunos colegas. ¿Hay una forma preferida de recuperar un objeto en Django cuando solo espera uno?
Las dos formas obvias son:
try:
obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# We have no object! Do something...
pass
Y:
objs = MyModel.objects.filter(id=1)
if len(objs) == 1:
obj = objs[0]
else:
# We have no object! Do something...
pass
El primer método parece conductualmente más correcto, pero utiliza excepciones en el flujo de control que pueden introducir algo de sobrecarga. El segundo es más indirecto, pero nunca planteará una excepción.
¿Alguna idea sobre cuál de estos es preferible? ¿Cuál es más eficiente?
QS.get()
es bueno. 2. Los detalles importan: ¿"esperar solo uno" significa siempre 0-1 objetos, o es posible tener 2+ objetos y ese caso también debería ser manejado (en este casolen(objs)
es una idea terrible)? 3. No asuma nada sobre los gastos generales sin un punto de referencia (creo que en este casotry/except
será más rápido siempre que al menos la mitad de las llamadas devuelvan algo)Puede instalar un módulo llamado django-molesto y luego hacer esto:
fuente
1 es correcto En Python, una excepción tiene una sobrecarga igual a un retorno. Para una prueba simplificada puedes mirar esto .
2 Esto es lo que Django está haciendo en el backend.
get
llamafilter
y genera una excepción si no se encuentra ningún elemento o si se encuentra más de un objeto.fuente
try
.Llego un poco tarde a la fiesta, pero con Django 1.6 existe el
first()
método en los conjuntos de consultas.https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Ejemplo:
fuente
No puedo hablar con ninguna experiencia de Django, pero la opción # 1 le dice claramente al sistema que está pidiendo 1 objeto, mientras que la segunda opción no. Esto significa que la opción n. ° 1 podría aprovechar más fácilmente los índices de la base de datos o la memoria caché, especialmente cuando no se garantiza que el atributo que está filtrando sea único.
Además (de nuevo, especulando) la segunda opción puede tener que crear algún tipo de colección de resultados u objeto iterador ya que la llamada filter () normalmente podría devolver muchas filas. Evitarías esto con get ().
Finalmente, la primera opción es más corta y omite la variable temporal adicional, solo una pequeña diferencia, pero cada pequeña ayuda.
fuente
¿Por qué todo eso funciona? Reemplace 4 líneas con 1 atajo incorporado. (Esto hace su propio intento / excepto).
fuente
Model.objects.get_or_create()
esAlgo más de información sobre excepciones. Si no son criados, no cuestan casi nada. Por lo tanto, si sabe que probablemente obtendrá un resultado, use la excepción, ya que al usar una expresión condicional usted paga el costo de verificar cada vez, sin importar qué. Por otro lado, cuestan un poco más que una expresión condicional cuando se generan, por lo que si espera no tener un resultado con cierta frecuencia (por ejemplo, el 30% del tiempo, si la memoria sirve), la verificación condicional resulta ser un poco más barato
Pero este es el ORM de Django, y probablemente el viaje de ida y vuelta a la base de datos, o incluso un resultado en caché, probablemente domine las características de rendimiento, por lo tanto, favorezca la legibilidad, en este caso, ya que espera exactamente un resultado, use
get()
.fuente
Jugué un poco con este problema y descubrí que la opción 2 ejecuta dos consultas SQL, que para una tarea tan simple es excesiva. Mira mi anotación:
Una versión equivalente que ejecuta una sola consulta es:
Al cambiar a este enfoque, pude reducir sustancialmente el número de consultas que ejecuta mi aplicación.
fuente
Pregunta interesante, pero para mí la opción # 2 apesta a optimización prematura. No estoy seguro de cuál es más eficiente, pero la opción # 1 ciertamente me parece y me parece más pitónica.
fuente
Sugiero un diseño diferente.
Si desea realizar una función en un posible resultado, podría derivar de QuerySet, de esta manera: http://djangosnippets.org/snippets/734/
El resultado es bastante impresionante, podrías por ejemplo:
Aquí, filter devuelve un conjunto de consultas vacío o un conjunto de consultas con un solo elemento. Sus funciones de conjunto de consultas personalizadas también son encadenables y reutilizables. Si desea realizarlo para todas sus entradas:
MyModel.objects.all().yourFunction()
.También son ideales para ser utilizados como acciones en la interfaz de administración:
fuente
La opción 1 es más elegante, pero asegúrese de usar try..except.
Según mi propia experiencia, puedo decirle que a veces está seguro de que no puede haber más de un objeto coincidente en la base de datos y, sin embargo, habrá dos ... (excepto, por supuesto, cuando obtiene el objeto por su clave principal).
fuente
Lamento agregar una versión más sobre este tema, pero estoy usando el paginador django, y en mi aplicación de administración de datos, el usuario puede elegir qué consultar. A veces, esa es la identificación de un documento, pero de lo contrario es una consulta general que devuelve más de un objeto, es decir, un conjunto de consultas.
Si el usuario consulta la identificación, puedo ejecutar:
que arroja un error en el paginador de django, porque es un registro y no un conjunto de consultas de registros.
Necesito correr:
Que devuelve un conjunto de consultas con un elemento en él. Entonces el paginador funciona bien.
fuente
.obtener()
.filtrar()
Nota
fuente