在Django中,对数据进行校验有两种方式:一种是通过Form中校验,一种是通过Model校验。在次,我对Model中的校验方法做下记录。

所有内容都是基于Django1.10的官网文档整理而来

validating objects

数据校验的触发:

Model中的校验是通过调用Model.full_clean()方法来执行的。包括在Form中也会对objects进行校验,也是通过调用Model.full_clean()的方式来进行的。但是,通常的情况下我们并不需要自己调用Model.full_clean()方法。

什么时候需要调用full_clean()

当你使用ModelForm的时候,Model.full_clean()将会在你调用is_valid()方法的时候对ModelForm中所有的field进行校验。只有当你想要自己特别的处理校验的报错信息,或者是想要校验在ModelForm中没有包含的field时才需要来自己调用full_clean()这个方法。

Model.full_clean说明

Model.full_clean(exclude=None, validate_unique=True)
Model.full_clean()内部其实是会通过3个方法来进行不同层次的校验,对于这3个方法在后面会讲到。

参数:

可选参数exclude可以用来指定不需要执行校验的field。ModelForm也利用这个参数来将field排除。
参数validate_unique用来指定是否需要执行Model.validate_unique()

如何检查校验结果:

full_clean将会按序执行3个方法,这3个方法如果校验失败的话,会将相关信息写到异常的message_dict属性中,并且抛出ValidationError异常。

save()执行的时候是不会自动调用full_clean()来进行校验的。

手动校验model的合法性:

from django.core.exceptions import ValidationError
try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass

3个校验方法:

  1. 校验model fields – Model.clean_fields()

  2. 校验整个model – Model.clean()

  3. 校验field的唯一性 – Model.validate_unique()

这几个步骤将会在调用model的full_clean()方法时执行,流程如下:

st=>start: Model.full_clean
e=>end: return
io=>inputoutput: rais ValidationError
sub1=>subroutine: Model.clean_fields
cond1=>condition: valid
sub2=>subroutine: Model.clean
cond2=>condition: valid
sub3=>subroutine: Model.validate_unique
cond3=>condition: valid

st->sub1->cond1(yes)->sub2->cond2(yes)->sub3->cond3(yes)->e
st->sub1->cond1(no)->io
st->sub1->cond1(yes)->sub2->cond2(no)->io
st->sub1->cond1(yes)->sub2->cond2(yes)->sub3->cond3(no)->io

Model.clean_fields

Model.clean_fields(exclude=None)
这个方法将会校验排除exclude中指定的,model中的所有field。当它校验失败的时候,会抛出ValidationError异常。

Model.clean

如果你想要自定义model的校验,或者想要修改model的属性的话,就override这个方法。例如,你可以使用它来为field自动提供一个值:

import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _

class Article(models.Model):
    ...
    def clean(self):
        # Don\'t allow draft entries to have a pub_date.
        if self.status == \'draft\' and self.pub_date is not None:
            raise ValidationError(_(\'Draft entries may not have a publication date.\'))
        # Set the pub_date for published items if it hasn\'t been set already.
        if self.status == \'published\' and self.pub_date is None:
            self.pub_date = datetime.date.today()

当调用model的save()方法的时候,是不会调用Model.clean来进行校验的

校验中的错误处理

在上面的例子中,我们使用了ValidationError来在Model.clean中抛出错误,这个错误信息将会存储在以NON_FIELD_ERRORS为key的字典中。这个key是用来存储对于整个model中的错误信息的。

如何获取校验的错误信息:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

如何指定对于某个特定的field的校验错误信息:

class Article(models.Model):
    ...
    def clean(self):
        # Don\'t allow draft entries to have a pub_date.
        if self.status == \'draft\' and self.pub_date is not None:
            raise ValidationError({\'pub_date\': _(\'Draft entries may not have a publication date.\')})
        ...

如何指定多个field的校验错误信息:

raise ValidationError({
    \'title\': ValidationError(_(\'Missing title.\'), code=\'required\'),
    \'pub_date\': ValidationError(_(\'Invalid date.\'), code=\'invalid\'),
})

Model.validate_unique说明

Model.validate_unique(exclude=None)
最后,full_clean()将会检查model中的unique的限制。它在校验失败的时候会抛出ValidationError异常。

注意

值得注意的是:上面多次提到,在执行save()方法的时候,是不会进行数据校验的。校验应该在save()执行之前完成,你可以先在form进行校验,也可以在model中进行校验。但是,你必须确保通过这两个校验之后的数据是绝对没有问题的“干净”数据,然后再调用save()方法将数据存储入库。