Validate in Model

Django 提供了model的实例validate(也就是数据库表的一行的验证), 这个功能由下面三块组成

1 Validate the model fields - Model.clean_fields()//验证每个field
2 Validate the model as a whole - Model.clean()// 验证整个model,这个是我们可以自己定制的
3 Validate the field uniqueness - Model.validate_unique() //验证field的唯一性

你可以通过显示调用实例的full_clean()方法调用上面三个验证,如果只调用了实例的save()方法,将不会调用验证

Model.full_clean(exclude=None, validate_unique=True)
该方法依次调用Model.clean_fields(),Model.clean(),Model.validate_unique()(validate_unique=True情况下才会调用validate_unique,默认是True)
如果验证过程中出现问题, 会产生ValidationError异常,该异常的属性message_dict包含以上三步中的所有异常
exclude 参数提供一个列表,该列表中列出的field会调过验证,默认是None
//下面直接上代码

 1       def full_clean(self, exclude=None, validate_unique=True):
 2         """
 3         Calls clean_fields, clean, and validate_unique, on the model,
 4         and raises a ``ValidationError`` for any errors that occurred.
 5         """
 6         errors = {}
 7         if exclude is None:
 8             exclude = []
 9         else:
10             exclude = list(exclude)
11
12         try:
13             self.clean_fields(exclude=exclude)   //调用clean_fields
14         except ValidationError as e:
15             errors = e.update_error_dict(errors)
16
17         # Form.clean() is run even if other validation fails, so do the
18         # same with Model.clean() for consistency.
19         try:
20             self.clean()    //调用clean
21         except ValidationError as e:
22             errors = e.update_error_dict(errors)
23
24         # Run unique checks, but only for fields that passed validation.
25         if validate_unique:
26             for name in errors.keys():
27                 if name != NON_FIELD_ERRORS and name not in exclude:  //对通过clean_fields的fields进行unique的检查
28                     exclude.append(name)
29             try:
30                 self.validate_unique(exclude=exclude)
31             except ValidationError as e:
32                 errors = e.update_error_dict(errors)
33
34         if errors:
35             raise ValidationError(errors)

Step1:clean_fields:
对每一个field进行clean,exclude包含不包括clean的field,可以看到本质上是对每一个field调用field.clean()

 1     def clean_fields(self, exclude=None):
 2         """
 3         Cleans all fields and raises a ValidationError containing a dict
 4         of all validation errors if any occur.
 5         """
 6         if exclude is None:
 7             exclude = []
 8
 9         errors = {}
10         for f in self._meta.fields:  //取出每一个fiels
11             if f.name in exclude:   //调过exclude
12                 continue
13             # Skip validation for empty fields with blank=True. The developer
14             # is responsible for making sure they have a valid value.
15             raw_value = getattr(self, f.attname)  //取出原始值
16             if f.blank and raw_value in f.empty_values:  //如果该field是可以空的,并且raw_value是空的,则过
17                 continue
18             try:
19                 setattr(self, f.attname, f.clean(raw_value, self))  //调用field.clean() 并将clean之后的值放到f.attname,此处意味着如果有些clean会改变属性的值比如+1,那么调用两次就会+2,感觉这地方是个bug
20             except ValidationError as e:
21                 errors[f.name] = e.error_list  //{‘field_name‘:‘field_error_list‘}
22
23         if errors:
24             raise ValidationError(errors)

我们再看一下fields.clean() 这个方法

 1     def clean(self, value):
 2         """
 3         Validates the given value and returns its "cleaned" value as an
 4         appropriate Python object.
 5
 6         Raises ValidationError for any errors.
 7         """
 8         value = self.to_python(value)  //将raw_value 转化为python类型
 9         self.validate(value)  //运行field.validate()
10         self.run_validators(value)  //运行feild.run_validators()
11         return value
12
13
14     def to_python(self, value):   //感觉这个方法什么事情都没做
15         return value
16
17     def validate(self, value):            //仅仅检测了对于必须的field是否为空,注意为空的判断self.empty_values
18         if value in self.empty_values and self.required:
19             raise ValidationError(self.error_messages[‘required‘], code=‘required‘)
20
21     def run_validators(self, value):        //对提供的validators进行验证。validators是field定义的时候定义的
22         if value in self.empty_values:
23             return
24         errors = []
25         for v in self.validators:
26             try:
27                 v(value)
28             except ValidationError as e:
29                 if hasattr(e, ‘code‘) and e.code in self.error_messages:
30                     e.message = self.error_messages[e.code]
31                 errors.extend(e.error_list)
32         if errors:
33             raise ValidationError(errors)    

Step2:Model.clean()
该方法由用户定制,进行模型的clean,代码里面也是空的

Step3.validate_unique
验证唯一性

 1     def validate_unique(self, exclude=None):
 2         """
 3         Checks unique constraints on the model and raises ``ValidationError``
 4         if any failed.
 5         """
 6         unique_checks, date_checks = self._get_unique_checks(exclude=exclude)  //可以看到分为unique_checks, date_checks
 7
 8         errors = self._perform_unique_checks(unique_checks)
 9         date_errors = self._perform_date_checks(date_checks)
10
11         for k, v in date_errors.items():
12             errors.setdefault(k, []).extend(v)
13
14         if errors:
15             raise ValidationError(errors)

我们看一下self._get_unique_checks(exclude=exclude) 这个方法,这个方法返回unique_checks和date_checks

 1  def _get_unique_checks(self, exclude=None):
 2         """
 3         Gather a list of checks to perform. Since validate_unique could be
 4         called from a ModelForm, some fields may have been excluded; we can‘t
 5         perform a unique check on a model that is missing fields involved
 6         in that check.
 7         Fields that did not validate should also be excluded, but they need
 8         to be passed in via the exclude argument.
 9         """
10         if exclude is None:
11             exclude = []
12         unique_checks = []
13
14         unique_togethers = [(self.__class__, self._meta.unique_together)]   //(class_name,uniq_list) unique_together不包括单独的field作为唯一性
15         for parent_class in self._meta.get_parent_list():
16             if parent_class._meta.unique_together:
17                 unique_togethers.append((parent_class, parent_class._meta.unique_together))   //[(class1,uniq1),(class2,uniq_2)]
18
19         for model_class, unique_together in unique_togethers:
20             for check in unique_together:                                                // uniq = [(uniq1,uniq2)] 其中uniq1=(file1,filed2,filed3),如果一个uniq的组里面有一个filed是exclude的则调过
21                 for name in check:
22                     # If this is an excluded field, don‘t add this check.
23                     if name in exclude:
24                         break
25                 else:
26                     unique_checks.append((model_class, tuple(check)))   //check = (field1,field2...)
27
28         # These are checks for the unique_for_<date/year/month>.
29         date_checks = []
30
31         # Gather a list of checks for fields declared as unique and add them to
32         # the list of checks.
33
34         fields_with_class = [(self.__class__, self._meta.local_fields)]
35         for parent_class in self._meta.get_parent_list():
36             fields_with_class.append((parent_class, parent_class._meta.local_fields))
37
38         for model_class, fields in fields_with_class:
39             for f in fields:
40                 name = f.name
41                 if name in exclude:
42                     continue
43                 if f.unique:
44                     unique_checks.append((model_class, (name,)))            //增加单独的唯一性的fields
45                 if f.unique_for_date and f.unique_for_date not in exclude:
46                     date_checks.append((model_class, ‘date‘, name, f.unique_for_date))
47                 if f.unique_for_year and f.unique_for_year not in exclude:
48                     date_checks.append((model_class, ‘year‘, name, f.unique_for_year))
49                 if f.unique_for_month and f.unique_for_month not in exclude:
50                     date_checks.append((model_class, ‘month‘, name, f.unique_for_month))
51         return unique_checks, date_checks
52
53   

执行uniq的check

 1      def _perform_unique_checks(self, unique_checks):
 2         errors = {}
 3
 4         for model_class, unique_check in unique_checks:
 5             # Try to look up an existing object with the same values as this
 6             # object‘s values for all the unique field.
 7
 8             lookup_kwargs = {}
 9             for field_name in unique_check:
10                 f = self._meta.get_field(field_name)  //获取field名称对应的field,这地方为什么不直接传field呢?
11                 lookup_value = getattr(self, f.attname)
12                 if lookup_value is None:
13                     # no value, skip the lookup
14                     continue
15                 if f.primary_key and not self._state.adding:   //如果其中有一个field是主键并且是editing的话则不需要检测,因为主键是不可更改的
16                     # no need to check for unique primary key when editing
17                     continue
18                 lookup_kwargs[str(field_name)] = lookup_value
19
20             # some fields were skipped, no reason to do the check  //前面某个field有continue的分支产生
21             if len(unique_check) != len(lookup_kwargs):
22                 continue
23
24             qs = model_class._default_manager.filter(**lookup_kwargs)  //构建查询对象
25
26             # Exclude the current object from the query if we are editing an
27             # instance (as opposed to creating a new one)
28             # Note that we need to use the pk as defined by model_class, not
29             # self.pk. These can be different fields because model inheritance
30             # allows single model to have effectively multiple primary keys.
31             # Refs #17615.
32             model_class_pk = self._get_pk_val(model_class._meta)
33             if not self._state.adding and model_class_pk is not None:
34                 qs = qs.exclude(pk=model_class_pk)
35             if qs.exists():                                //如果qs存在,则表示有重复
36                 if len(unique_check) == 1:
37                     key = unique_check[0]
38                 else:
39                     key = NON_FIELD_ERRORS
40                 errors.setdefault(key, []).append(self.unique_error_message(model_class, unique_check))
41
42         return errors

#检测日期是否相同

 1     def _perform_date_checks(self, date_checks):
 2         errors = {}
 3         for model_class, lookup_type, field, unique_for in date_checks:
 4             lookup_kwargs = {}
 5             # there‘s a ticket to add a date lookup, we can remove this special
 6             # case if that makes it‘s way in
 7             date = getattr(self, unique_for)
 8             if date is None:
 9                 continue
10             if lookup_type == ‘date‘:
11                 lookup_kwargs[‘%s__day‘ % unique_for] = date.day
12                 lookup_kwargs[‘%s__month‘ % unique_for] = date.month
13                 lookup_kwargs[‘%s__year‘ % unique_for] = date.year
14             else:
15                 lookup_kwargs[‘%s__%s‘ % (unique_for, lookup_type)] = getattr(date, lookup_type)
16             lookup_kwargs[field] = getattr(self, field)
17
18             qs = model_class._default_manager.filter(**lookup_kwargs)
19             # Exclude the current object from the query if we are editing an
20             # instance (as opposed to creating a new one)
21             if not self._state.adding and self.pk is not None:
22                 qs = qs.exclude(pk=self.pk)
23
24             if qs.exists():
25                 errors.setdefault(field, []).append(
26                     self.date_error_message(lookup_type, field, unique_for)
27                 )
28         return errors

可以看到,在做validate的时候避免的很多不必要的validate,比如update的时候,当field是主键的时候就没有去做uniq的检查

时间: 2024-11-06 17:32:03

Validate in Model的相关文章

Django中的一些同步Model和数据库的命令

1 最简单而又令人兴奋的命令: manage.py syncdb 创建了指定app中的model相应的数据库表,假设是第一次使用此命令,会提示是否创建超级用户,输入username,Email和password,接着能够看到在创建索引: 2. validate 验证Model的正确性:manage.py validate,若Model所有有效,会提示:0 errors found. 3. sqlall [appname,....] 打印指定app的CREATE TABLE的语句,包含原始数据,创

Model的验证

ModelValidator与ModelValidatorProvider ModelValidator public abstract class ModelValidator { public virtual bool IsRequired { get { return false; } } public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules()//用于客户端验证 { return E

ASP.NET MVC Model验证学习—上

蒋大师的MVC框架解析确实是越学越有趣,即使是跟着学写些示例代码也是收获良多,尤其是关于类型.反射和委托等方面,平时在应用开发中确实很少会有机会写这样的代码.今天学习的ASP.NET MVC中的Model的验证,刚开时会以为这一章会比较简单,因为之前已经学习过了Model元数据的解析.Model绑定,Model的验证可能就只是DataAnnotation相关类的介绍.但实际学习的过程中,尤其是自定义用于修饰Action的验证特性让我到现在仍然感觉是比较萌萌哒,毕竟这一块对于框架的扩展基本上涉及到

[水煮 ASP.NET Web API2 方法论](1-6)Model Validation

问题 想要 ASP.NET Web API 执行模型验证,同时可以和 ASP.NET MVC 共享一些验证逻辑. 解决方案 ASP.NET Web API 与 ASP.NET MVC 支持一样的验证机制,都是通过System.ComponentModel.DataAnnoataions 的属性验证.使用框架提供的相关验证属性,已足够来用来验证模型. 想要更细粒度的验证,我们可以选择在我们的模型中实现 IValudateObject(来自于System.ComponentModel.DataAnn

iOS项目中Json转Model的坑

Json转Model json转model,是个开发都会遇到过.都已经9102年了,谁还不会用个第三方框架搞.拿起键盘就是干!打开podfile,把大名顶顶的YYModel写上,pod install一下.再用上ESJsonFormat,直接根据json,都能把model生成好. 特殊处理 啥?返回的字段值不是我们所需的在日常开发中,经常会遇到一些接口字段返回的值,并不是我所需要的类型的情况,这个时候,我们都会对这个字段进行处理.举个栗子: 123456 /** 错误代码 */@property

Bsim3 学习笔记11

Model Testing Requirements for a MOSFET Model in Circuit Simulation (1) It should include most or all of the important physical effects in modern MOSFETs. (2) The model should meet the requirements for accuracy and continuity of the I-V equations and

rails提供的validators

转载自: http://www.cnblogs.com/lhyun/p/3448740.html Instance Public methods attribute_method?(attribute)Link Returns true if attribute is an attribute method, false otherwise. class Person include ActiveModel::Validations attr_accessor :name end User.at

django之概述

1.创建model, 2.然后再setting里面加入这个app,即激活模型 3.然后用命令:manage.py validate确认model是否有错误, 4.python manage.py makemigrations polls,建表,迁移文件, 5.python manage.py sqlmigrate book 0001用来运行步骤4的迁移文件,把django所要做的事情打印到屏幕上,并不改变数据库表,也可以运行python manage.py check命令:它能在你没有执行迁移或

yii2源码学习笔记(七)

今天继续了解model类 1 1 /** 2 2 * Returns the form name that this model class should use. 3 3 * 4 4 * 返回表单的名称,就是这个 model 的类名 5 5 * 6 6 * The form name is mainly used by [[\yii\widgets\ActiveForm]] to determine how to name 7 7 * the input fields for the attr