Как сделать автообновление категории по количеству товаров в модели django

У меня есть модель категории с заголовком и подсчетом, и у меня также есть другая модель под названием Products, которая имеет категорию в качестве одного из своих внешних ключей. Как я могу автоматически обновить поле category count по количеству продуктов, относящихся к той же категории?

class Category(models.Model):
    title = models.CharField(max_length = 20)
    count = models.IntegerFIeld()

class Product(models.Model):
    title = models.CharField(max_length = 20)
    category = models.ForeignKey(Category, on_delete = models.CASCADE)

Вы можете либо переопределить метод сохранения, либо написать сигнал для автоматического обновления поля, т.е.:

class Category(models.Model):
    def save(self, *args, **kwargs):
        self.count = self.product_set.count()
        super().save(*args, **kwargs)

или с помощью сигналов:

from django.dispatch import receiver
@receiver(post_save, sender=Category)
def category_post_save(sender, instance, *args, **kwargs):
    instance.count = instance.product_set.count()
    instance.save()

Но я бы посоветовал вам использовать расчетное property:

class Category(models.Model):
    ...

    @property
    def count(self):
        return self.product_set.count()

Если вы хотите сохранить поле count, я рекомендую вам использовать signals. На модели post_signalProductpre_signal следует использовать и (для получения предыдущей категории из запроса).

Вот сигналы:

@receiver(signals.pre_save, sender=Product)
def product_pre_save_signal(sender, instance, *args, **kwargs):
    if instance.id:
        previous_instance = Product.objects.get(id=instance.id)
        if previous_instance.category != instance.category:
            instance.previous_category = previous_instance.category

@receiver(signals.post_save, sender=Product)
def product_post_save_signal(sender, instance, created, *args, **kwargs):
    if not created:
        if hasattr(instance, "previous_category"):
            if instance.previous_category:
                instance.previous_category.count -= 1
                instance.previous_category.save()
            if instance.category:
                instance.category.count += 1
                instance.category.save()
    else:
        if instance.category:
            instance.category.count += 1
            instance.category.save()

Другая рекомендация для этой ситуации - сделать поле доступным только для чтения (добавить editable=False к объявлению поля), чтобы никто не мог редактировать его из админки Django или сериализатора.

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

У вас может быть практически любой запрос, связанный с полем count над Категорией или Продуктом, так что не беспокойтесь:)

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