Cómo agrupar y agregar con Django

82

Tengo una consulta bastante simple que me gustaría hacer a través del ORM, pero no puedo resolverlo.

Tengo tres modelos:

Ubicación (un lugar), Atributo (un atributo que podría tener un lugar) y Calificación (un modelo M2M 'a través' que también contiene un campo de puntuación)

Quiero elegir algunos atributos importantes y poder clasificar mis ubicaciones según esos atributos, es decir, una puntuación total más alta sobre todos los atributos seleccionados = mejor.

Puedo usar el siguiente SQL para obtener lo que quiero:

select location_id, sum(score) 
    from locations_rating 
    where attribute_id in (1,2,3) 
    group by location_id order by sum desc;

que regresa

 location_id | sum 
-------------+-----
          21 |  12
           3 |  11

Lo más cerca que puedo estar con el ORM es:

Rating.objects.filter(
    attribute__in=attributes).annotate(
    acount=Count('location')).aggregate(Sum('score'))

Que vuelve

{'score__sum': 23}

es decir, la suma de todos, no agrupados por ubicación.

¿Alguna forma de evitar esto? Podría ejecutar el SQL manualmente, pero prefiero ir a través del ORM para mantener la coherencia.

Gracias

Guy Bowden
fuente

Respuestas:

135

Prueba esto:

Rating.objects.filter(attribute__in=attributes) \
    .values('location') \
    .annotate(score = Sum('score')) \
    .order_by('-score')
Aamir Adnan
fuente
3
hmmm - anotar, no agregar - ¿por qué?
Guy Bowden
51
aggregatees para el conjunto de resultados completo, annotatepara filas individuales (agrupadas).
Bouke
¿Alguien sabe cómo solucionarlo 'dict' object has no attribute '_meta'con ese QuerySet?
maciek
Hola @Aamir, tengo un problema similar, ¿puedes echarle un vistazo? enlace-aquí
Ermir Beqiraj
Devuelve algo como[{"location": 53, "score": 100}, {"location": 54, "score": 104}]
Irvan
38

¿Puedes probar esto?

Rating.objects.values('location_id').filter(attribute__in=attributes).annotate(sum_score=Sum('score')).
Srinivas Reddy Thatiparthy
fuente