4. Как обновить денормализованные поля в других моделях при сохранении?¶
У вас есть такие модели.
class Category(models.Model):
    name = models.CharField(max_length=100)
    hero_count = models.PositiveIntegerField()
    villain_count = models.PositiveIntegerField()
    class Meta:
        verbose_name_plural = "Categories"
class Hero(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    # ...
class Villain(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    # ...
Вам нужно, чтобы hero_count и villain_count обновлялись при создании новых объектов.
Вы можете сделать что-то вроде этого
class Hero(models.Model):
    # ...
    def save(self, *args, **kwargs):
        if not self.pk:
            Category.objects.filter(pk=self.category_id).update(hero_count=F('hero_count')+1)
        super().save(*args, **kwargs)
class Villain(models.Model):
    # ...
    def save(self, *args, **kwargs):
        if not self.pk:
            Category.objects.filter(pk=self.category_id).update(villain_count=F('villain_count')+1)
        super().save(*args, **kwargs)
Обратите внимание, что мы не использовали self.category.hero_count += 1, так как update будет выполнять обновление БД.
Альтернативным методом является использование signals. Вы можете сделать это следующим образом.
from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=Hero, dispatch_uid="update_hero_count")
def update_hero_count(sender, **kwargs):
    hero = kwargs['instance']
    if hero.pk:
        Category.objects.filter(pk=hero.category_id).update(hero_count=F('hero_count')+1)
@receiver(pre_save, sender=Villain, dispatch_uid="update_villain_count")
def update_villain_count(sender, **kwargs):
    villain = kwargs['instance']
    if villain.pk:
        Category.objects.filter(pk=villain.category_id).update(villain_count=F('villain_count')+1)
4.1. Сигналы против переопределения .save¶
Поскольку любой из сигналов .save может быть использован для поведения сохранения, когда следует использовать какой из них? Я следую простому правилу.
- Если ваши поля зависят от модели, которую вы контролируете, переопределите - .save.
- Если ваши поля зависят от модели стороннего приложения, которое вы не контролируете, используйте сигналы.