Django : Пересчет среднего значения в базе данных после создания нового экземпляра

У меня есть информация о компаниях, представленная в таблице. Одно из полей этой таблицы - среднее значение каждой полученной компанией заметки ('note_moyenne' в models.FicheIdentification). Нажав на кнопку, люди могут отправить новую заметку для компании ('note' в models.EvaluationGenerale). Я хочу, чтобы среднее значение заметок обновлялось в базе данных каждый раз, когда кто-то отправляет новую заметку.

Вот мой models.py :

class FicheIdentification(models.Model):
    entreprise=models.ForeignKey(Entreprise, on_delete=models.CASCADE)
    note_moyenne=models.IntegerField()

    def __str__(self):
        return self.entreprise.nom_entreprise

class EvaluationGenerale(models.Model):
    entreprise=models.ForeignKey(Entreprise, on_delete=models.CASCADE)
    note=models.IntegerField()
    commentaires=models.CharField(max_length=1000)
    date_evaluation=models.DateField(auto_now_add=True)

    def __str__(self):
        return self.commentaires

views.py :

class CreerEvaluationGenerale(CreateView):
    form_class = FormulaireEvaluationGenerale
    model = EvaluationGenerale
    def form_valid(self, form):
        form.instance.entreprise=Entreprise.objects.filter(siret=self.kwargs['siret']).first()
        return super(CreerEvaluationGenerale, self).form_valid(form)
    def get_success_url(self):
        return reverse('details-evaluations')

В настоящее время я просто отображаю среднее значение в моей таблице, используя следующее

def render_evaluation(self, record):
        return (EvaluationGenerale.objects.filter(entreprise=record.entreprise.siret).aggregate(Avg('note'))['note__avg'])

но мне не нравится это решение, поскольку я хочу, чтобы значение хранилось в базе данных, в FicheIdentification.note_moyenne.

Я думал о создании класса UpdateView, но не смог связать его с моим CreateView.

Любая помощь или документация будут очень признательны, я немного запутался...

Я вижу два способа сделать это.

Либо слушатель post_save на EvaluationGenerale (doc). Вы сможете вычислять новое среднее значение каждый раз, когда новый EvaluationGenerale будет введен в DB.

@receiver(post_save, sender=EvaluationGenerale)
def evaluation_general_note_moyenne_computer_post_save_listener(sender, instance, **kwargs):
    entreprise = instance.entreprise 
    entreprise.note_moyenne = entreprise.evaluationgeneral_set.aggregate(Avg('note')).values()[0])
    entreprise.save()

слушатель после сохранения сработает только на instance.save() и models.objects.create(), а не на queryset.update() или model.objects.bulk_create().

Либо переопределите функцию сохранения (doc) вашей формы для вычисления среднего значения после создания нового EvaluationGenerale

def save(self):
    instance = super.save()
    entreprise = instance.entreprise 
    entreprise.note_moyenne = entreprise.evaluationgeneral_set.aggregate(Avg('note')).values()[0]
    entreprise.save() 
    return instance

Предполагая, что для каждого предприятия существует единственный объект FicheIdentification, вы можете обновить поле note_moyenne при сохранении объекта EvaluationGenerale, например:

obj = FicheIdentification(...)

FicheIdentification.objects.filter(entreprise=record.entreprise.siret).update(note_moyenne=obj.aggregate(Avg('note'))['note__avg']

obj.save()

Пожалуйста, дайте мне знать, если это сработает.

Обычно вы не храните вычисляемые поля. Обычный способ - не хранить среднее значение, а использовать аннотацию/агрегацию в запросе.

Чтобы централизовать это в вашей модели, вы захотите написать пользовательский менеджер модели для реализации этого, чтобы его можно было повторно использовать везде, где вы используете вашу модель, не переписывая логику.

class MyModelManager(models.Manager):
    def note_average(self, **filter_kwargs):
        qs = self.get_queryset()
        # replace `...` with your aggregation as needed
        return qs.filter(**filter_kwargs).aggregate(...) 

class EvaluationGenerale(models.Model):
    objects = MyModelManager()  # override the default manager
    # ... the rest of the model as-is

Тогда вы можете использовать что-то вроде следующего в вашем представлении(ях):

EvaluationGenerale.objects.note_average(entreprise=record.entreprise.siret)

Для получения дополнительной информации см: Как добавить вычисляемое поле в модель Django

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