Оптимизированный способ обработки элементов с пользовательскими оценками?
Я работаю над сайтом, который позволяет пользователям оценивать предметы, например, книги. Оценка должна отображаться на странице книги, а также на коллективной странице, содержащей все книги. Оценки должны быть доступны для изменения:
class BookUserGrade:
user = foreign_key_to_user
book = foreign_key_to_book
grade = ...
Однако операция запроса оценок при каждом обращении пользователя к книжному списку (или, в меньшей степени, к странице одной книги) кажется дорогостоящей.
Я размышляю над тем, чтобы сделать два дополнительных поля в самой книге, number_of_grades
и cumulative_grade
, которые будут обновляться только при оценке книги пользователем. Возможно, также calculated_grade
(стоит ли добавлять это поле вместо того, чтобы вычислять cumulative_grade / number_of_grades
на лету?
Или, может быть, есть другой правильный способ сделать это?
Обычно хранить вычисляемые поля в базе данных - плохая практика. Это приводит к большим накладным расходам, связанным с необходимостью следить за тем, чтобы все оставалось синхронизированным.
Есть несколько различных способов решить эту проблему, если вас беспокоит, насколько это дорого, то наиболее эффективным с точки зрения вычислений способом достижения этой цели является разгрузка работы в базе данных путем написания пользовательского менеджера, который аннотирует совокупности оценок для каждой книги, например
class GradedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
cumulative_grade=models.Sum('bookusergrade__grade'),
grade_count=models.Count('bookusergrade')
)
class Book(models.Model):
name = models.CharField(max_length=100)
objects = models.Manager()
graded = GradedManager()
class BookUserGrade(models.Model):
user = models.CharField(max_length=100)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
grade = models.IntegerField()
Тогда при запросе к этому менеджеру, например, с помощью Book.graded.all(), каждая книга в наборе запросов будет иметь два дополнительных поля 'cumulative_grade' и 'grade_count'