registro - sentencias sql en django
Filtro de Django frente a obtener para un solo objeto? (11)
Estaba teniendo un debate sobre esto con algunos colegas. ¿Hay una forma preferida de recuperar un objeto en Django cuando esperas solo 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 ser más correcto desde el punto de vista del comportamiento, pero utiliza excepciones en el flujo de control, lo que puede generar cierta sobrecarga. El segundo es más indirecto pero nunca levantará una excepción.
¿Alguna idea de cuál de estos es preferible? ¿Cuál es más eficiente?
¿Por qué todo eso funciona? Reemplace 4 líneas con 1 atajo incorporado. (Esto hace su propio intento / excepto).
from django.shortcuts import get_object_or_404
obj = get_object_or_404(MyModel, id=1)
1 es correcto En Python, una excepción tiene una sobrecarga igual a un retorno. Para una prueba simplificada, puedes ver this .
2 Esto es lo que Django está haciendo en el back-end. get
llamadas filter
y genera una excepción si no se encuentra ningún elemento o si se encuentra más de un objeto.
Algo más de información sobre excepciones. Si no se crían, 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, paga el costo de verificar cada vez, pase lo que pase. Por otro lado, cuestan un poco más que una expresión condicional cuando se generan, por lo que si esperas no tener un resultado con cierta frecuencia (por ejemplo, 30% del tiempo, si la memoria sirve), se obtiene la verificación condicional ser un poco mas 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é, domine las características de rendimiento, por lo que debe favorecer la legibilidad, en este caso, ya que espera exactamente un resultado, use get()
.
He jugado un poco con este problema y descubrí que la opción 2 ejecuta dos consultas SQL, lo que para una tarea tan simple es excesivo. Ver mi anotación:
objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
obj = objs[0] # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else:
# we have no object! do something
pass
Una versión equivalente que ejecuta una sola consulta es:
items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
return None
return items[0]
Al cambiar a este enfoque, pude reducir sustancialmente el número de consultas que ejecuta mi aplicación.
Interesante pregunta, pero para mí la opción # 2 huele a optimización prematura. No estoy seguro de cuál es más eficiente, pero la opción n. ° 1 ciertamente se ve y se siente más pitónica para mí.
La opción 1 es más elegante, pero asegúrese de usar try ... excepto.
Desde mi propia experiencia puedo decirte que a veces estás 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, al obtener el objeto por su clave principal).
Llego un poco tarde a la fiesta, pero con Django 1.6 existe el first()
método first()
en los conjuntos de consulta.
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first
Devuelve el primer objeto que coincide con el conjunto de consultas, o Ninguno si no hay ningún objeto coincidente. Si QuerySet no tiene ningún orden definido, entonces el conjunto de consulta se ordena automáticamente por la clave primaria.
Ejemplo:
p = Article.objects.order_by(''title'', ''pub_date'').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:
try:
p = Article.objects.order_by(''title'', ''pub_date'')[0]
except IndexError:
p = None
No puedo hablar con ninguna experiencia de Django, pero la opción n. ° 1 le dice claramente al sistema que usted está pidiendo 1 objeto, mientras que la segunda opción no. Esto significa que la opción n. ° 1 podría aprovecharse más fácilmente de los índices de caché o de base de datos, especialmente donde no se garantiza que el atributo que está filtrando sea único.
También (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 al filtro () 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 todo ayuda.
Puede instalar un módulo llamado django-annoying y luego hacer esto:
from annoying.functions import get_object_or_None
obj = get_object_or_None(MyModel, id=1)
if not obj:
#omg the object was not found do some error stuff
Sugiero un diseño diferente.
Si desea realizar una función en un posible resultado, puede derivar de QuerySet, así: http://djangosnippets.org/snippets/734/
El resultado es bastante impresionante, podrías por ejemplo:
MyModel.objects.filter(id=1).yourFunction()
Aquí, el filtro devuelve un conjunto de consulta vacío o un conjunto de consulta con un solo elemento. Sus funciones de conjunto de preguntas 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:
def yourAction(self, request, queryset):
queryset.yourFunction()
get()
se proporciona específicamente para este caso. Úselo.
La opción 2 es casi exactamente cómo el método get()
se implementa en realidad en Django, por lo que no debería haber diferencia de "rendimiento" (y el hecho de que estés pensando en ello indica que estás violando una de las reglas cardinales de la programación, es decir, intentar optimizar el código incluso antes de que se haya escrito y perfilado; hasta que tengas el código y puedas ejecutarlo, no sabes cómo funcionará, y tratar de optimizarlo antes es un camino de dolor).