Аннотация Django возвращает 1 для каждого элемента
У меня есть 2 почти одинаковые модели.
class FavoriteBook(models.Model):
class Meta:
# Model for books added as favorites
verbose_name = "Favorite Book"
unique_together = ['user', 'book']
user = models.ForeignKey(User, null=False, blank=False, on_delete=models.CASCADE, verbose_name="User", related_name="favorite_books")
book = models.ForeignKey(Book, null=False, blank=False, on_delete=models.CASCADE, verbose_name="Book")
def __str__(self):
return "{user} added {book} to favorites".format(user=self.user, book=self.book)
class SpoilerVote(models.Model):
class Meta:
# Model for votes of book spoilers
verbose_name = "Spoiler Vote"
unique_together = ['spoiler', 'user']
spoiler = models.ForeignKey(Spoiler, null=False, blank=False, on_delete=models.CASCADE, verbose_name="Spoiler")
user = models.ForeignKey(User, null=False, blank=False, on_delete=models.CASCADE, verbose_name="User", related_name="bookspoiler_votes")
choices = (
('U', 'UP'),
('D', 'DOWN'),
)
vote = models.CharField(max_length=1, choices=choices, null=False, blank=False, verbose_name="Vote")
def __str__(self):
return "{user} liked {book}'s spoiler".format(user=self.user, book=self.spoiler.book.book_title)
Следующий запрос работает нормально для модели FavoriteBook.
most_popular = FavoriteBook.objects.values("book", "book__book_title", "book__year").annotate(count=Count("book")).order_by("-count")[:20]
Но этот вариант не работает для модели SpoilerVote. Он возвращает 1 для каждого элемента.
SpoilerVote.objects.values("spoiler", "user", "vote").annotate(count=Count("spoiler")).order_by("-count")
Что я упускаю? Нет никакой разницы.
Проблема с текущим запросом
В вашем запросе SpoilerVote:
SpoilerVote.objects.values("spoiler", "user", "vote").annotate(count=Count("spoiler")).order_by("-count")
Вы используете .values("spoiler", "user", "vote"), который группирует по спойлеру, пользователю и голосованию. Это означает, что каждая комбинация спойлера, пользователя и голоса будет подсчитана, что приведет к подсчету 1 для каждой строки, потому что группировка происходит на более детальном уровне, чем вы хотите. Решение
Чтобы получить количество голосов за каждый спойлер и упорядочить их по этому количеству, нужно группировать только по спойлерам. Вот исправленный запрос:
from django.db.models import Count
# Aggregate and order by the count of votes for each spoiler
most_voted_spoilers = SpoilerVote.objects.values("spoiler", "spoiler__book__book_title").annotate(count=Count("spoiler")).order_by("-count")
Объяснение
.values("spoiler", "spoiler__book__book_title"): Это указывает Django группировать результаты по спойлеру, а также включать название книги, связанное со спойлером, для более легкого понимания результата.
.annotate(count=Count("spoiler")): Это добавляет подсчет голосов для каждого спойлера. Поскольку мы группируем по спойлерам, этот подсчет отражает общее количество голосов, полученных каждым спойлером.
.order_by("-count"): Упорядочивает результаты по счету в порядке убывания, так что наиболее проголосовавшие спойлеры оказываются на первом месте.
С помощью этого скорректированного запроса вы должны получить список спойлеров, упорядоченный по количеству набранных ими голосов, подобно тому, как вы получили список самых популярных книг в модели FavoriteBook.