通过 m2m_changed 信号处理程序中的模型额外字段访问 Django

Access Django through model extra fields in m2m_changed signal handler

假设我有以下 Django 模型,它们表示父子之间的排序关系:

class Parent(models.Model):
    name = models.CharField(max_length=50)
    children = models.ManyToManyField("Child", through="ParentChild")

class Child(models.Model):
    name = models.CharField(max_length=50)

class ParentChild(models.Model):
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["parent", "child"], name="uc_parent_child"),
            models.UniqueConstraint(fields=["parent", "sort_number"], name="uc_parent_child"),
        ]

    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
    child = models.ForeignKey(Child, on_delete=models.CASCADE)
    sort_number = models.IntegerField()

    def save(self, *args, **kwargs):
        exising_sort_numbers = self.parent.parentchild_set.values_list(
            "sort_number", flat=True
        )
        if self.sort_number in exising_sort_numbers:
            raise Exception(f"Duplicate sort number: {self.sort_number}")
        super().save(*args, **kwargs)

现在,如果我使用直通模型创建关系,我会得到重复的异常 sort_number:

ParentChild.objects.create(parent=parent, child=child1, sort_number=0)
ParentChild.objects.create(parent=parent, child=child2, sort_number=0)  # raises Exception

但是,如果我使用 .add 方法创建关系,则不会出现异常:

parent.children.add(child1, through_defaults={"sort_number": 0})
parent.children.add(child2, through_defaults={"sort_number": 0})  # does NOT raise Exception

我知道using the .add method doesn't call the .save method on the through model所以我需要使用m2m_change信号来运行这个逻辑。但我不确定如何在这个信号中获得 sort_number 。到目前为止,这是我的信号代码:

@receiver(m2m_changed, sender=Parent.children.through)
def validate_something(sender, instance, action, reverse, model, pk_set, **kwargs):
    if action == "pre_add":
        for pk in pk_set:
            child = model.objects.get(pk=pk)
            exising_sort_numbers = instance.parentchild_set.values_list(
                "sort_number", flat=True
            )
            # where's sort_number specified in through_defaults ???

知道如何获取此值并执行 "pre_add" 验证,或者这是不可能的吗?

你有这个约束 - models.UniqueConstraint(fields=["parent", "sort_number"], name="uc_parent_child"),这意味着你不能有超过一个相同的 parentsort_number 的关系。 ParentChildsave 方法中甚至有一个额外的检查来进一步执行此操作。当您尝试创建这样的关系时抛出异常是有道理的。

此外,约束名称需要是唯一的。我尝试了代码,但无法按原样进行迁移。

如果您按照自己的意愿行事,您将在保存时再次遇到该异常。 您应该 change/remove 它或调整您的代码以使用它,而不是尝试绕过约束 - 不要尝试创建会违反它的实例。

关于您的具体问题,您在 validate_something 中获得的实例是 Parent 并且无法直接访问中间实例或它是默认实例。您也无法查询中间实例,因为它还不存在。