Как группировать по в отношениях 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')
Но в данном конкретном случае, возможно, имеет смысл аннотировать Song
s:
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))
)