Pregunta

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 usa 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?

¿Fue útil?

Solución

get () se proporciona específicamente para este caso. Úselo.

La opción 2 es casi con precisión cómo se implementa el método get () en Django, por lo que no debería haber "rendimiento". diferencia (y el hecho de que esté pensando en ello indica que está violando una de las reglas cardinales de programación, a saber, tratar de optimizar el código incluso antes de que se haya escrito y perfilado) hasta que tenga el código y pueda ejecutarlo, usted no sé cómo funcionará, y tratar de optimizar antes es un camino de dolor).

Otros consejos

Puede instalar un módulo llamado django-annoying y luego haga 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

1 es correcto. En Python, una excepción tiene una sobrecarga igual a un retorno. Para obtener una prueba simplificada, puede consultar esto .

2 Esto es lo que Django está haciendo en el backend. get llama al filter y genera una excepción si no se encuentra ningún elemento o si se encuentra más de un objeto.

Llego un poco tarde a la fiesta, pero con Django 1.6 existe el método first () en los conjuntos de consultas.

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 el QuerySet no tiene un orden definido, entonces el conjunto de consultas se ordena automáticamente por la clave principal.

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 # 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 caché o de la base de datos, especialmente cuando no se garantiza que el atributo que está filtrando sea único.

Además (nuevamente, 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.

¿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)

Más 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, así que favorezca la legibilidad, en este caso, ya que espera exactamente un resultado, use < code> 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. Vea 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.

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.

Sugiero un diseño diferente.

Si desea realizar una función en un posible resultado, puede derivar de QuerySet, como este: 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 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:

def yourAction(self, request, queryset):
    queryset.yourFunction()

La opción 1 es más elegante, pero asegúrese de usar try..except.

Desde 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 primario clave).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top