通过 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")
,这意味着你不能有超过一个相同的 parent
和 sort_number
的关系。 ParentChild
的 save
方法中甚至有一个额外的检查来进一步执行此操作。当您尝试创建这样的关系时抛出异常是有道理的。
此外,约束名称需要是唯一的。我尝试了代码,但无法按原样进行迁移。
如果您按照自己的意愿行事,您将在保存时再次遇到该异常。
您应该 change/remove 它或调整您的代码以使用它,而不是尝试绕过约束 - 不要尝试创建会违反它的实例。
关于您的具体问题,您在 validate_something
中获得的实例是 Parent
并且无法直接访问中间实例或它是默认实例。您也无法查询中间实例,因为它还不存在。
假设我有以下 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")
,这意味着你不能有超过一个相同的 parent
和 sort_number
的关系。 ParentChild
的 save
方法中甚至有一个额外的检查来进一步执行此操作。当您尝试创建这样的关系时抛出异常是有道理的。
此外,约束名称需要是唯一的。我尝试了代码,但无法按原样进行迁移。
如果您按照自己的意愿行事,您将在保存时再次遇到该异常。 您应该 change/remove 它或调整您的代码以使用它,而不是尝试绕过约束 - 不要尝试创建会违反它的实例。
关于您的具体问题,您在 validate_something
中获得的实例是 Parent
并且无法直接访问中间实例或它是默认实例。您也无法查询中间实例,因为它还不存在。