Исходные данные:
Три модели, первая
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'))))
)
Но где-то ошибаюсь, не могу понять, в правильном ли я направлении иду?