1

在标题中,我的意思是等于任何值,而不仅仅是其他空值。

假设我们有一个家庭,有家庭成员(人)和电子设备。有些电子设备是个人的,属于一个人,但有些不属于任何一个家庭成员,例如书房中的一台普通计算机。然后我们可以像这样对电子设备进行建模:

class ElectronicDevice(models.Model):
    name = models.CharField(null=False, max_length=64)
    owner = models.ForeignKey(HouseMember, null=True, on_delete=models.CASCADE)

现在假设我们希望设备名称从家庭成员的角度来看是唯一的。一个家庭成员不应该有两个具有相同名称的设备可供他们使用。所以 name 和 owner 一起应该是唯一的,但也不应该有两个设备,一个 private 和一个 common(owner 为 null)具有相同的名称。

因此一个正常的UniqueConstraint

class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', 'owner'], name='uniqe_device_name'),
        ]

不会这样做,因为如果其中一个设备的所有者为空,它仍然允许两个设备具有相同的名称。仅约束名称字段:

class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', ], name='uniqe_device_name'),
        ]

也不会这样做,因为这不允许两个家庭成员将他们的私人设备命名相同,尽管我们确实希望允许这样做。

我目前的尝试是限制名称和所有者的唯一性,然后CheckConstraint如果已经存在具有该名称的通用设备(所有者为空),则使用不允许名称:

class Meta:
        constraints = [
            models.UniqueConstraint(fields=['name', 'owner'], name='uniqe_device_name'),
            models.CheckConstraint(check=???, name='no_common_device_with_same_name')
        ]

这是这里最好的方法还是有更好的解决方案?如果这是最好的解决方案:如何为此编写CheckConstraint

4

1 回答 1

2

可能你可以这样尝试:

constraints = [ 
    models.UniqueConstraint(fields=['name', 'owner'], condition = Q(owner__isnull=False), name='uniqe_device_name'),
    models.UniqueConstraint(fields=['name'], condition = Q(owner__isnull=True), name='uniqe_device_name_without_owner')
]

更多信息可以在文档中找到。

更新

如果您需要无法从约束中处理的自定义条件,那么最好覆盖保存方法。例如:

class ElectronicDevice(models.Model):
    ...
    def save(self, *args, **kwargs):
        if some conditions:
           raise ValidationError()
        super().save(*args, **kwargs)
于 2021-03-23T12:32:02.407 回答