Оптимизированный способ обработки элементов с пользовательскими оценками?

Я работаю над сайтом, который позволяет пользователям оценивать предметы, например, книги. Оценка должна отображаться на странице книги, а также на коллективной странице, содержащей все книги. Оценки должны быть доступны для изменения:

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'

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