2010-04-05 17 views
12

Me sorprende que esta pregunta no haya surgido. No se pudo encontrar mucho en la web.Django equivalente para la última entrada para cada usuario

Utilizando Entry.objects.latest('created_at') Puedo recuperar la última entrada para todos los objetos de entrada, pero decir si deseo la última entrada para cada usuario? Esto es algo similar a una última consulta de registro de SQL. Pero, ¿cómo logro esto usando el ORM? Aquí está mi enfoque Me pregunto si es la forma más eficiente de hacer lo que quiero.

Primero realizo una sub consulta: Los objetos se agrupan por usuario y se devuelve el campo Max (última) created_by para cada usuario (created_at__max) Luego filtro los objetos Entry basados ​​en los resultados en la subconsulta y obtengo los objetos requeridos.

Entry.objects.filter(created_at__in=Entry.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

o el uso de un gestor de:

class UsersLatest(models.Manager): 

    def get_query_set(self): 
     return super(UsersLatest,self).get_query_set().filter(created_at__in=self.model.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

¿Hay una manera más eficiente? posiblemente sin sub consulta?

Gracias,

Paul

+1

que estaba jugando con la idea de añadir un booleano is_latest a mi modelo, la cual di al guardar una nueva entrada en mi método form.save, que luego se recupera el objeto is_latest anterior y lo hará falsa --- por lo que hay hay dos pasos para guardar cada entrada. No sé si esta es la mejor manera de hacerlo. ¿Alguna sugerencia? – Paul

Respuesta

0

No puedo pensar en una sola consulta SQL en bruto que obtener el conjunto que necesita, por lo que dudo que sea posible construir un QuerySet con estos resultados.

1

El diseño de su QuerySet depende de para qué lo vaya a usar. No estoy seguro de por qué está saliendo del iterador QuerySet con el método values_list al final. Me imagino que tiene una lista de estado de usuarios donde muestra la última actividad según el modelo de Entradas. Para que es posible que desee probar esto:

Users.objects.all().annotate(latest_activity=Max('entries__created_at')) 

Y a continuación, recorrer sus usuarios fácilmente en su plantilla con

{% for user in users %} 
{{ user.full_name }} 
{{ user.latest_activity|date: "m/d/Y" }} 
{% endfor %} 
+0

Hola gracias por tu sugerencia, acercamiento agradable. annotate (Max ('created_at')) devuelve una lista de diccionarios que contienen datos de 'usuario' y 'created_at__max', yo uso values_list ('created_at__max') para filtrar los datos en una lista para usar con created_at__in = – Paul

1

El SQL prima sería

SELECT entry.id, entry.title, entry.content, entry.user_id, entry.created_at 
FROM 
    entry 
WHERE 
    entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id) 

Así que una opción es utilizando el argumento where del extra() modifier:

Entry.objects.extra(where='entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id)') 

Por supuesto, probablemente tenga que cambiar entry a cualquiera que sea el nombre real de la tabla en la base de datos. Suponiendo que se sienta cómodo mirando ._meta, puede intentar esto:

Entry.objects.extra(where= 
    '%(table)s.created_at = (SELECT Max(e2.created_at) from %(table)s as e2 where e2.user_id = %(table)s.user_id)' % { 'table':Entry._meta.db_table } 
) 

Probablemente hay una forma más elegante para obtener el nombre de una tabla.

+0

Gracias por sugerir , hace exactamente lo que requiero he comprobado el sql de mi consulta de Django usando ._as_sql() y es casi exactamente el mismo – Paul

0

tuve un problema similar y lo hizo de esta manera:

priorities = obj.books_set.values('category').annotate(priority=Max('priority')) 

Nota: anoto prioridad máxima como prioridad, porque voy a volver a utilizar la salida como condición de filtro.

Es una lista de categorías con prioridades mínimas.Entonces hago esto:

>>> priorities[0] 
{'category': 1, 'priority': 10} 

Quiero encontrar libros que tienen categoría & par prioridad entre uno de la lista. Al final, la condición queryset debería tener este aspecto:

Q(priorities[0]) | Q(priorities[1]) | ... 

Para hacer esto en una línea, utilice reduce en Q.__or__:

reduce(Q.__or__, (Q(**x) for x in priorities)) 

Sé que es un poco peor que SQL crudo, pero más seguro. Comentar el código si lo usa, porque es difícil de leer.

3

Usando order_by y distinct:

Entry.objects.all().order_by('user', 'created_at').distinct('user') 

Entonces para un rendimiento añadiendo índice juntos en 'usuario' y los campos de los created_at '.

pero creo modo de producción real es utilizar Redis para almacenar en caché y actualizar la lista de un id de las entradas más recientes de los usuarios.

Cuestiones relacionadas