Django 2 个字段之一不能为空

Posted

技术标签:

【中文标题】Django 2 个字段之一不能为空【英文标题】:Django one of 2 fields must not be null 【发布时间】:2019-04-04 18:06:37 【问题描述】:

我有一个类似的模型:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

field1field2 至少有一个字段不能为空。如何在模型中验证它?

【问题讨论】:

【参考方案1】:

你可以使用Model.clean()方法:

def clean(self):
    if self.field1 is None and self.field2 is None:
        raise ValidationError(_('field1 or field2 should not be null'))

见https://docs.djangoproject.com/en/2.1/ref/models/instances/#django.db.models.Model.clean

【讨论】:

【参考方案2】:

Model.clean

通常在Model.clean [Django-doc] 中编写此类测试:

from django.core.exceptions import ValidationError

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    def clean(self):
        super().clean()
        if self.field1 is None and self.field2 is None:
            raise ValidationError('Field1 or field2 are both None')

请注意,当您.save() 模型时,默认情况下验证此 clean 方法。它通常仅由构建在此模型之上的ModelForms 调用。当您 .save() 模型实例时,您可以修补 .save() 方法 for example like here 以强制验证,但仍有一些方法可以通过 ORM规避

django-db-constraints(部分数据库不支持)

如果您的数据库支持它(例如 mysql simply ignores the CHECK constraints),SQL 提供了一种语法来添加额外的约束,而 Django 包 django-db-constraints [GitHub] 提供了一些工具来指定此类约束,例如:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    class Meta:
        db_constraints = 
            'field_null': 'CHECK (field1 IS NOT NULL OR field2 IS NOT NULL)',
        

更新:Django 约束框架

由于django-2.2,您可以使用Django constraint framework [Django-doc]。使用此框架,您可以指定数据库约束,如果数据库支持这一点,则在数据库端进行验证。因此,您可以检查两个字段中的至少一个是否不是NULLCheckConstraint [Django-doc]:

class Person(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    field1= models.IntegerField(null=True)
    field2 = models.IntegerField(null=True)

    class Meta:
        constraints = [
            models.CheckConstraint(
                check=Q(field1__isnull=False) | Q(field2__isnull=False),
                name='not_both_null'
            )
        ]

【讨论】:

如果要定义两者之一(field1 或 field2),我们该怎么办。两者都不能为空,也不能定义。这类似于异或(XOR),但models.Q 似乎不支持这一点。 @ShubhamDhingra:你可以把它转换成析取范式,所以Q(field1__isnull=False, field2=None) | Q(field1=None, field2__isnull=False)【参考方案3】:

从 Django 2.2 开始,您可以在 Django 中使用built-in constraints 功能。无需 3rd 方代码。

【讨论】:

以上是关于Django 2 个字段之一不能为空的主要内容,如果未能解决你的问题,请参考以下文章

Django 模型 - 字段依赖

Django 内置字段

Django Form 内置字段

Django的Field(字段)

18_django的用户模型和扩展django的用户模型

jQuery下拉框,不能使字段值为空