假设我有以下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
方法中甚至还有一个额外的检查,以进一步强制执行此操作。在尝试创建这种关系时抛出异常是很有意义的。此外,约束名称必须唯一。我尝试了代码,无法按原样进行迁移。
如果执行您要尝试的操作,则保存时会再次收到该异常。而不是试图破解约束,您应该更改/删除该约束或使您的代码适合于使用该约束-不要尝试创建会违反该约束的实例。
关于您的特定问题,您在validate_something
中获得的实例为Parent
,并且无法直接访问中间实例,或者它是默认实例。您也无法查询中介实例,因为它尚不存在。