Django 发出多对多变更信号

Chi*_*fir 5 python django many-to-many signals

我有3个型号:

class Product(TimeStampedModel):
    category = models.ForeignKey('Category', related_name='products', to_field='category_name')
    brand = models.ForeignKey('Brand', related_name='products', to_field='brand_name')

class Brand(models.Model):    
    brand_name = models.CharField(max_length=50)
    categories = models.ManyToManyField('Category', related_name='categories')

class Category(models.Model):
    category_name = models.CharField(max_length=128)
Run Code Online (Sandbox Code Playgroud)

我想Brand-Category在更改后更新 M2M 关系Product.category
我尝试连接信号,就像文档m2m_changed中描述的那样:

@receiver(m2m_changed, sender=Brand.categories.through)
def category_changed(sender, **kwargs):
    print("Signal connected!")
Run Code Online (Sandbox Code Playgroud)

我还注册signalapps.py一个project_folder

  def ready(self):
        from my_app.signals import category_changed
Run Code Online (Sandbox Code Playgroud)

但问题是——这段代码没有任何效果。我改变了Product.category- 并且没有看到任何打印。我应该如何修复它才能使其正常工作?

Jur*_*dil 8

这是处理 Many2Many 字段更改的正确方法 Django Docs

from django.db.models.signals import m2m_changed

m2m_changed.connect(category_changed,sender=Brand.categories.through)
Run Code Online (Sandbox Code Playgroud)

编辑:我没有意识到 Chiefir 正在请求 QuerySet更新方法上的信号,为此我们不能使用内置 m2m_changed 信号。正如拉尔夫在之前的回答中指出的那样,在更新时触发信号可能会导致性能问题,例如在收听保存的直通的情况下。然而,有一个解决方案,恕我直言,不会带来性能问题。

本质上,您可以重写查询集更新方法,以在每次更新整个查询集时触发自定义信号。每次查询集更新只会触发一次,您可以通过检查更新参数将其指定为仅在一个特定字段更新时触发。

该信号现在发送正在更新的查询集和作为有效负载的值。请注意,在此代码中,信号是在类别更新之前发送的。要改变这一点,请将超级输出保存为变量,分派信号并返回变量。

from django.dispatch import Signal, receiver

product_category_updated = Signal(providing_args=["queryset", "value"])


class ProductQuerySet(models.QuerySet):

    def update(self, *args, **kwargs):
        if 'category' in kwargs:
            product_category_updated.send(sender=self.__class__, queryset=self, value=kwargs.get('category'))
        return super(ProductQuerySet, self).update(*args, **kwargs)

class ProductManager(models.Manager):

    def get_queryset(self, show_hidden=False):
        return ProductQuerySet(self.model, using=self._db, hints=self._hints)

class Product(TimeStampedModel):
    objects = ProductManager()

    category = models.ForeignKey('Category', related_name='products', to_field='category_name', on_delete=models.deletion.CASCADE)
    brand = models.ForeignKey('Brand', related_name='products', to_field='brand_name', on_delete=models.deletion.CASCADE)

class Brand(models.Model):
    brand_name = models.CharField(max_length=50, unique=True)
    categories = models.ManyToManyField('Category', related_name='categories')

class Category(models.Model):
    category_name = models.CharField(max_length=128, unique=True)

@receiver(product_category_updated, sender=ProductQuerySet)
def category_changed(sender, **kwargs):
    print("Signal connected!")
Run Code Online (Sandbox Code Playgroud)

我必须通过在foreignkey字段上添加on_delete属性并使brand_name和category_name唯一来调整代码以适应我的Django 2.0.7版本。