Jak grupować przez AND agregować za pomocą Django

83

Mam dość proste zapytanie, które chciałbym wykonać za pośrednictwem ORM, ale nie mogę tego rozgryźć.

Mam trzy modele:

Lokalizacja (miejsce), atrybut (atrybut, który może mieć miejsce) i ocena (model M2M „do”, który zawiera również pole punktacji)

Chcę wybrać kilka ważnych atrybutów i móc uszeregować moje lokalizacje według tych atrybutów - tj. Wyższy wynik całkowity dla wszystkich wybranych atrybutów = lepszy.

Mogę użyć następującego SQL, aby uzyskać to, czego chcę:

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

który powraca

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

Najbliższe, co mogę uzyskać dzięki ORM, to:

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

Który powraca

{'score__sum': 23}

tj. suma wszystkich, nie pogrupowanych według lokalizacji.

Jakoś to obejść? Mógłbym wykonać SQL ręcznie, ale wolałbym przejść przez ORM, aby zachować spójność.

Dzięki

Guy Bowden
źródło

Odpowiedzi:

136

Spróbuj tego:

Rating.objects.filter(attribute__in=attributes) \
    .values('location') \
    .annotate(score = Sum('score')) \
    .order_by('-score')
Aamir Adnan
źródło
3
hmmm - adnotacja nie agregacja - dlaczego tak jest?
Guy Bowden
51
aggregatedotyczy pełnego zestawu wyników annotatedla pojedynczych (zgrupowanych) wierszy.
Bouke,
czy ktoś wie, jak obejść 'dict' object has no attribute '_meta'taki zestaw QuerySet?
maciek
Cześć @Aamir, mam podobny problem, możesz na to spojrzeć? link-tutaj
Ermir Beqiraj
Zwraca coś w stylu[{"location": 53, "score": 100}, {"location": 54, "score": 104}]
Irvan
38

Możesz tego spróbować.

Rating.objects.values('location_id').filter(attribute__in=attributes).annotate(sum_score=Sum('score')).
Srinivas Reddy Thatiparthy
źródło