【问题标题】:Access Django through model extra fields in m2m_changed signal handler通过 m2m_changed 信号处理程序中的模型额外字段访问 Django
【发布时间】:2020-06-12 02:18:38
【问题描述】:

假设我有以下 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" 验证,还是这不可能?

【问题讨论】:

    标签: django django-models django-signals


    【解决方案1】:

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

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

    如果您按照自己的方式进行操作,保存时会再次出现该异常。 与其试图绕过约束,您应该更改/删除它或调整您的代码以使用它 - 不要尝试创建会违反它的实例。

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

    【讨论】:

    • 很遗憾,无法在信号处理程序中获取sort_number。如果我想执行我无法在数据库级别执行的验证怎么办。就像sort_number 不能大于10
    • ^ 这可能不是最好的例子,因为在 Django 模型级别有​​一些 hacky 方法可以做到这一点。更好的例子是强制排序号必须比现有排序号的最大值大一(例如exising_sort_numbers = [0, 1, 2] 然后sort_number 必须是3
    猜你喜欢
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    • 2015-07-09
    • 2015-06-03
    • 2014-02-09
    • 2019-09-30
    相关资源
    最近更新 更多