Аннотация 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.

Вернуться на верх