【问题标题】:how to check to ManyToManyField table to update m2m depend on a parent field django如何检查 ManyToManyField 表以更新 m2m 取决于父字段 django
【发布时间】:2020-11-22 22:39:26
【问题描述】:

我想根据其父模型更新 m2m 字段,例如:

class ModelA(models.Model):
    status = models.BooleanField(default=True)
    status_of_product = models.ManyToManyField(Product, verbose_name='product')

class Product(models.Model):
    active = models.BooleanField(default=False)
    products = models.CharField(max_length=30, unique=True)

现在这会生成一个名为 modela_status_of_product 的单独表。

只有当ModelA 中的status 字段等于True 时,我才想更新Product 中的active 字段。

def update_product_m2m(sender, instance, **kwargs):
    if instance.status == True: #this is wrong and doesnt work 
        Product.objects.filter(pk__in=kwargs.get('pk_set')).update(active=True)

m2m_changed.connect(update_product_m2m, sender=ModelA.status_of_product.through)

请问可以吗?感谢您的帮助。

【问题讨论】:

    标签: python django m2m


    【解决方案1】:

    根据您实际使用 M2M 关系的方式,将以不同方式调用侦听器。你必须在你的听众中检查它。照原样,您的侦听器被称为更新前后、删除前后以及清除前后。我展示的版本只是在更新后做一些事情,但它知道如何处理关系的正向和反向使用。

    这可能还不够,但我认为这是一个很好的起点。您肯定会希望根据您的业务逻辑以不同的方式处理“post_delete”和“post_clear”(也许产品在失去最后一个状态为 True 的 ModelA 后需要变为非活动状态)

    阅读 m2m_changed 信号的完整文档以获取更多信息。

    def update_product_m2m(sender, instance, action, reverse, pk_set, **kwargs):
        if action != "post_add":
            return
    
        if reverse:  # product.modela_set.add(modela)
            # instance is the product being modified
            # pk_set are the modelas being added
            if ModelA.objects.filter(pk__in=pk_set, status=True).exists():
                instance.active=True
                instance.save()
    
        else:  # modela.status_of_product.add(product)
            # instance is the modela being modified.
            # pk_set are the product ids being added
            if instance.status is True:
                Product.objects.filter(pk__in=pk_set).update(active=True)
    

    无论如何,instance 绝不是“直通表”。它是 ModelA 的实例或 Product 的实例,具体取决于 m2m 的更新方式。

    来源: https://docs.djangoproject.com/en/3.1/ref/signals/#m2m-changed

    如果这还不足以解决您的问题,请发布回溯。

    【讨论】:

      【解决方案2】:
      def update_product_m2m(sender, **kwargs):
          instance = kwargs.pop('instance', None)
          if instance and instance.status == True:
              instance.status_of_product.update(active=True)
      
      m2m_changed.connect(update_product_m2m, sender=ModelA.status_of_product.through)
      

      或:

      class ModelA(models.Model):
       ...
         def save(self):
             if self.pk and self.status:
                self.status_of_product.update(active=True)
             super().save()
      

      【讨论】:

      • 感谢您的回复,但我认为它不起作用,因为我想更新产品模型并且m2m表没有instance.status
      • 尝试第二种解决方案,并以单数形式打印实例的类型,通常是ModelA的实例,以及sender Prodcut。
      • 行不通,product是一个M2M领域我们得想办法包含pk__in=pk_set
      最近更新 更多