Агрегат делает дополнительные запросы
В моей модели есть класс книг и рецензий, и я хочу вычислить средний рейтинг. Для этого я использую aggregate и Avg
models.py
class Book(models.Model):
author = models.ManyToManyField(Author, related_name='books')
title = models.CharField(max_length=200)
description = models.TextField(max_length=3000)
price = models.DecimalField(max_digits=6, decimal_places=2)
publisher = models.CharField(max_length=200)
language = models.CharField(max_length=200)
pages = models.PositiveSmallIntegerField()
isbn = models.CharField(max_length=13, validators=[validate_isbn(), MaxLengthValidator(13)])
cover_image = models.ImageField(upload_to='books/images')
publish = models.BooleanField(default=True)
@property
def average_rating(self):
avg_rating = Review.objects.filter(book_id=self.id).aggregate(Avg('rating'))
return avg_rating['rating__avg']
def __str__(self):
return self.title
class Review(models.Model):
RATING = [
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
]
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='reviews')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
description = models.TextField()
rating = models.PositiveSmallIntegerField(choices=RATING, default=5)
date_added = models.DateField(auto_now_add=True)
objects = ReviewManager()
def __str__(self):
return f"{self.user.username} {self.book.title}"
Теперь для каждой книги у меня есть один дополнительный запрос
Как я могу решить эту проблему?
посмотрите select_related
и prefetch_related
Review.objects.filter(book_id=self.id).prefetch_related('author').select_related('author__book', 'author__user').aggregate(Avg('rating'))
Вы можете использовать values() для запроса. как указано в документации https://docs.djangoproject.com/en/4.1/ref/models/querysets/#django.db.models.query.QuerySet.values
from django.db.models import Avg
qs = Book.objects.values("id").annotate(average_rating=Avg('reviews__rating'))
Результат будет аналогичен
qs
<QuerySet [{'id': 1, 'average_rating': 5.0}, {'id': 2, 'average_rating': 2.0}]>
Я использовал аннотацию в ViewSet и моя проблема решилась
views.py
class BookViewSet(ModelViewSet):
http_method_names = ["get", "post", "patch", "delete"]
queryset = Book.objects.prefetch_related('author').annotate(average_rating=Avg('reviews__rating'))
def get_serializer_class(self):
if self.action == 'list':
return BookSerializer
else:
return BookDetailSerializer
serializer.py
class BookSerializer(serializers.ModelSerializer):
average_rating = serializers.FloatField()
isbn = serializers.IntegerField()
class Meta:
model = Book
fields = [
"id",
"author",
"title",
"description",
"price",
"publisher",
"language",
"pages",
"isbn",
'publish',
"cover_image",
'average_rating',
]