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