@Doomplex

Как в Django составить запрос в функции для добавления среднего значения в queryset?

Исходные данные:
Три модели, первая Review, вторая Rating, третья RatingStar

Review :

class Review(models.Model):
    ...
    def get_vote_count(self):
        vote_count = Rating.objects.filter(review=self).aggregate(Count('star'))['star__count']
        return vote_count

    def get_average_rating(self):
        if self.get_vote_count() == 0:
            average_rating = 'Нет оценок'
            return average_rating
        else:
            average_rating = round(
                Rating.objects
                .filter(review=self)
                .aggregate(Sum('star'))['star__sum'] /
                self.get_vote_count(), 1)
            return average_rating


RatingStar:

class RatingStar(models.Model):
    """Звезды рейтинга."""
    value = models.SmallIntegerField('Значение', default=1)

    def __str__(self):
        return f'{self.value}'


Rating:

class Rating(models.Model):
    """"Рейтинг."""
    ...
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='Автор оценки',
        help_text='Оставить оценку данного обзора'
    )
    star = models.ForeignKey(
        RatingStar,
        on_delete=models.CASCADE,
        verbose_name='Звезда'
    )
    review = models.ForeignKey(
        Review,
        related_name='ratings',
        on_delete=models.CASCADE,
        verbose_name='Обзор'
    )


У меня в шаблоне, присутствует список, где выводится средний рейтинг каждого экземляра класса Review, и данное значение получается через методы класса Review:

def get_vote_count(self):
        vote_count = Rating.objects.filter(review=self).aggregate(Count('star'))['star__count']
        return vote_count

    def get_average_rating(self):
        if self.get_vote_count() == 0:
            average_rating = 'Нет оценок'
            return average_rating
        else:
            average_rating = round(
                Rating.objects
                .filter(review=self)
                .aggregate(Sum('star'))['star__sum'] /
                self.get_vote_count(), 1)
            return average_rating


Функция которая выводит в шаблон все значения, выглядит вот так:

def review_list(request, slug=None):
    category = Category.objects.filter(slug=slug)
    reviews = Review.objects.filter(status=1)
    reviews_list = Category.objects.get(slug=slug).posts.all()
    if slug:
        category = get_object_or_404(Category, slug=slug)
        sub_categories = category.get_descendants(include_self=True)
        reviews_list = reviews.filter(category__in=sub_categories)
    paginator = PostPaginator(reviews_list, settings.SHOW_COUNT_POSTS)
    page_obj = paginator.show_count_post(request)
    context = {
        'page_obj': page_obj,
        'category': category,

    }
    return render(request, 'reviews/reviews_list.html', context)

Но т.к. при формировании в шаблоне списка и вывода среднего значения, мне приходится часто запрашиват это значение
{% for review in page_obj %}
    {{ review.get_vote_count }}
{% endfor %}

, что создает нагрузку на БД. Как правильно один раз сформировать единый queryset, что потом из него вынимать это значение(средний рейтинг) ?

Я пробовал сделать вот так:

reviews = Review.objects.annotate(average_rating=(
        Subquery(Rating.objects.aggregate(Sum('star'))) /
        Subquery(Rating.objects.aggregate(Count('star'))))
    )


Но где-то ошибаюсь, не могу понять, в правильном ли я направлении иду?
  • Вопрос задан
  • 107 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы