Django: Incrementar la cuenta de la entrada de blog por uno ¿Es esto eficiente?

Tengo el siguiente código en mi vista de índice.

latest_entry_list = Entry.objects.filter(is_published=True).order_by('-date_published')[:10] for entry in latest_entry_list: entry.views = entry.views + 1 entry.save() 

Si hay diez (el límite) filas devueltas de la consulta inicial, ¿el problema de guardar 10 separará las llamadas actualizadas a la base de datos, o es Django “lo suficientemente inteligente” como para emitir una sola llamada de actualización?

¿Existe un método más eficiente para lograr este resultado?

Puedes usar objetos F() para esto.

Así es como importa F : from django.db.models import F

Nuevo en Django 1.1 .
Las llamadas a actualizar también pueden usar objetos F () para actualizar un campo según el valor de otro campo en el modelo. Esto es especialmente útil para incrementar los contadores según su valor actual.

 Entry.objects.filter(is_published=True).update(views=F('views')+1) 

Aunque no puede realizar una actualización en un conjunto de consultas segmentadas … editar: en realidad puede …

Esto se puede hacer completamente en ORM de django. Necesita dos consultas SQL:

  1. Haga su filtro y recoja una lista de claves primarias
  2. Realice una actualización en un conjunto de consulta no segmentado de elementos que coincidan con cualquiera de esas claves primarias.

Obtener el conjunto de consultas no segmentado es el bit duro. Me pregunté sobre el uso de in_bulk pero eso devuelve un diccionario, no un conjunto de consultas. Usualmente, uno usaría los Q objects para hacer consultas complejas de tipo OR y eso funcionará, pero pk__in hace el trabajo mucho más simple.

 latest_entry_ids = Entry.objects.filter(is_published=True)\ .order_by('-date_published') .values_list('id', flat=True)[:10] non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_ids) n = non_sliced_query_set.update(views=F('views')+1) print n or 0, 'items updated' 

Debido a la forma en que django ejecuta las consultas perezosamente, esto da como resultado solo 2 visitas a la base de datos, sin importar cuántos elementos se actualicen.

Podría manejar las actualizaciones en una sola transacción, lo que podría mejorar significativamente el rendimiento. Utilice una función separada, decorada con @ transaction.commit_manually.

 @transaction.commit_manually def update_latest_entries(latest_entry_list): for entry in latest_entry_list: entry.views += 1 entry.save() transaction.commit() 

Revisado

Estás actualizando 10 objetos separados, individuales, distintos.

Las 10 actualizaciones separadas, individuales y distintas no se pueden contraer fácilmente en una actualización mágica que de alguna manera toque 10 objetos.

Si realmente necesita la eficiencia, en el momento tendría que ir a SQL y ejecutar la actualización usted mismo. Sin embargo, no vale la pena la complejidad agregada en este caso.

Con Django 1.1, podrá hacer esto en una sola llamada SQL a través del ORM utilizando objetos F () para hacer referencia a los campos en el valor de actualización.

Una mejora de rendimiento a la entrada anterior. Esto resulta en un hit de base de datos, con una subconsulta.

 latest_entry_query_set = Entry.objects.filter(is_published=True) .order_by('-date_published')[:10] non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_query_set.values('id')) n = non_sliced_query_set.update(views=F('views')+1) print n or 0, 'items updated'