Как группировать по в отношениях ManyToMany в Django?

Я создаю веб-приложение с Django и у меня возникли некоторые проблемы с использованием его ORM для создания запросов. У меня есть такие модели:

class Country(models.Model):
name = models.CharField(max_length=25)

class Song(models.Model):
title = models.CharField(max_length = 25)
country = models.ForeignKey(Country, on_delete=models.CASCADE, related_name="songs")
ratings = models.ManyToManyField(Country, through='Rating')

class Rating(models.Model):
rating = models.PositiveSmallIntegerField()
country_id = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True)
song_id = models.ForeignKey(Song, on_delete=models.SET_NULL, null=True)

Страна оценивает множество песен (с рейтингом 1-10), а песня может быть оценена многими странами, поэтому между этими моделями существует связь ManyToMany через таблицу Rating. Я делаю запрос, чтобы получить количество 10 баллов, которые получила песня, но не знаю как. Чтобы сделать это, я попробовал следующий запрос:

result = Rating.objects.filter(rating=10).values('song_id').annotate(ten_points = Count('rating'))

Я читал документацию Django и понял, что метод values() похож на group by clause в SQL, но это не работает, потому что этот запрос возвращает кверисет словарей следующим образом:

<QuerySet [{'song_id': 1, 'ten_points': 1}, {'song_id': 2, 'ten_points': 1}, {'song_id': 2, 'ten_points': 1}, {'song_id': 3, 'ten_points': 1}, {'song_id': 3, 'ten_points': 1} 

Почему я получаю разные словари с одинаковым ключом и значением 1, вместо словаря с ключом и значением общее количество десяти очков?

Необходимо использовать .order_by(…) для принудительной группировки, так:

result = Rating.objects.filter(rating=10).values(
    'song_id'
).annotate(ten_points=Count('rating')).order_by('song_id')

Но в данном конкретном случае, возможно, имеет смысл аннотировать Songs:

Song.objects.filter(
    rating__rating=10
).annotate(
    ten_points=Count('rating')
)

Это будет включать только те Song, которые имеют хотя бы одну Rating запись с rating=10. Если вы хотите аннотировать все Song, вы можете использовать:

from django.db.models import Q

Song.objects.annotate(
    ten_points=Count('rating', filter=Q(rating__rating=10))
)
Вернуться на верх