在Django中的字段中添加额外的约束

Vie*_*iet 29 django django-models django-forms

在进行子类化时db.models.Model,有时需要添加额外的检查/约束.

例如,我有一个Eventstart_date和的模型end_date.

我想在字段或模型中添加验证,以便end_date > start_date.

有多少种方法可以做到这一点?

至少我知道这可以models.ModelModelForm验证内部完成.

但是如何依附于田野和models.Model

ste*_*anw 51

我不会在save方法中加入这样的约束,为时已晚.在那里引发异常对于以错误方式输入数据的用户没有帮助,因为它最终会以500结尾,并且用户将无法获得返回错误的表单等.

您应该在Forms/ModelForms clean方法中检查这一点并引发ValidationError,因此form.is_valid()返回false并且您可以将表单中的错误发送回用户进行更正.

另请注意,自1.2版以来,Django已经进行了模型验证.

它看起来像这样:

class Foo(models.Model):
    #  ... model stuff...
    def clean(self):
        if self.start_date > self.end_date:
            raise ValidationError('Start date is after end date')
Run Code Online (Sandbox Code Playgroud)

  • 感谢第二个/更好的(?)答案.这正是我所需要的. (2认同)
  • 当然,这仅在您使用“ModelForm”或手动调用“is_valid”时才有效。否则,如果您只是调用“save”,[它不会*什么*](/sf/ask/310907761/)。 (2认同)
  • 从 Django 2.2 开始,您还可以从模型的 `Meta` 类中添加数据库级别的约束。https://docs.djangoproject.com/en/2.2/releases/2.2/#constraints (2认同)

Rei*_*ica 20

从 Django 2.2 开始,constraints支持数据库级别:

from django.db import models
from django.db.models import CheckConstraint, Q, F

class Event(models.Model):
    start_date = models.DatetimeField() 
    end_date = models.DatetimeField()

    class Meta:
        constraints = [
            CheckConstraint(
                check = Q(end_date__gt=F('start_date')), 
                name = 'check_start_date',
            ),
        ]
Run Code Online (Sandbox Code Playgroud)

  • 这就是我几个小时以来一直在寻找的答案:)因此对于其他人来说,重要的一点是您可以使用 F('other_field') 访问其他字段的值,从而在数据库级别上进行比较约束,很棒的东西:)但仍然需要验证,因为单独的约束会引发错误(服务器上为 500),但它保证数据库将拒绝作为最后的手段。 (5认同)
  • 这是目前正确的答案!现在使用Django的人应该都知道这个功能! (3认同)

Ser*_*nko 11

在您的模型的保存方法中执行此操作:

def save(self, *args, **kwargs):
    if(self.end_date > self.start_date):
        super(Foo, self).save(*args, **kwargs)
    else:
        raise Exception, "end_date should be greater than start_date" 
Run Code Online (Sandbox Code Playgroud)

  • 在django 1.2或更高版本中记得将*args,**kwargs添加到overriden save()方法的定义中以及它被调用的任何地方 (4认同)
  • 恕我直言,检查保存模型很好,但不够.您应该始终在尽可能低的级别施加限制:在这种情况下,您希望数据库阻止存储任何违反约束的值. (2认同)

Mar*_*ark 10

正如@stefanw所说,检查表单的干净方法是更好的用户体验.

如果你非常确定没有,也永远不会是另一种改变价值的方法,这就足够了.但由于您很少能够确定,如果数据库一致性很重要,您可以添加另一个检查(除了表单),其中一个:

  • @ umnik700表示,模型的保存方法更容易和数据库无关.请注意,这仍然不会阻止数据库的其他用户(另一个应用程序或管理界面)创建不一致的状态.
  • 要"完全"确保数据库是一致的,您可以添加数据库级别约束.例如,您可以使用RunSQL和SQL 创建迁移,类似于(未测试):

    migrations.RunSQL('ALTER TABLE app_event ADD CONSTRAINT chronology CHECK (start_date > end_date);')
    
    Run Code Online (Sandbox Code Playgroud)

    (未经测试).这可能与数据库有关,当然这是一个缺点.

在您的示例中,它可能不值得(不正确的开始/结束时间看起来有点奇怪,但只影响一个不一致的事件),并且您不希望手动更改架构.但它在一致性至关重要的情况下很有用.

编辑:您也可以只保存开始时间和持续时间,而不是开始和结束时间.