Django 1.8 验证器(Validator)

题记

继续深入学习 Django 中,其实在项目经验之上,有很多类似的需求会需要实现:在项目的模型逻辑或者视图逻辑中,其实会有很多交叉校验的逻辑,一般从原始的实现方式里面可能会采取如下的方式实现:

  1. 在数据库层采用触发器或者约束等方法来限制;
  2. 在模型层的动作里面(例如 save 或者 update)里面手动添加校验;
  3. 在视图层手工校验表单。

等等。

但是上述方法都很不优美:其一,验证的逻辑会散落在不同的地方,很难有效管理,查找修改都不方便;其二,很难复用相同的逻辑,如果真的要用手工的方式抽取代码,复用方式很难达成共识,以至于增加了架构层面的复杂度。

Django 是一个很大的框架,以致于同样的问题上面已经充分深思熟虑并且提供了解决方案,我们大可以去学习这个框架之下的解决方案。这个解决方案高度文档化而且设计优美,在同一个框架的使用者之间容易达成共识。

因此,大树之下好乘凉,我们要学习这个轮子,爬到 Django 这辆车上面,然后在上面把自己各种幼稚的实现产生的包袱扔下,更好地实现我们的应用。


验证器

编写验证器

验证器是一个可调用的对象(译注:函数或类),它获取一个值,然后在不满足一定条件的情况下抛出 ValidationError 的异常。验证器对于在不同模型字段中重用验证逻辑可以非常有用。

例如下面是一个只允许偶数的验证器:

from django.core.exceptions import ValidationError

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError('%s is not an even number' % value)

可以在模型字段的 validators 参数中将其加进去:

from django.db import models

class MyModel(models.Model):
    even_field = models.IntegerField(validators=[validate_even])

因为验证器运行之前,(输入的)数据会被转换为 Python 对象,因此我们甚至可以将同样的验证器用在 Django form 表单中:

from django import forms

class MyForm(forms.Form):
    even_field = forms.IntegerField(validators=[validate_even])

你也可以在类的 __call__() 方法中来构造更复杂或者可配置的验证器。例如 RegexValidator 就使用了这种技术。如果一个基于类的验证器使用了 validators 模型选项,你应当确保它可以通过添加 deconstruct()__eq__() 方法,被 migration 引擎序列化 https://docs.djangoproject.com/en/1.8/topics/migrations/#migration-serializing。(译注,非常拗口,现保留,后面理解再解释)

后补解释:

参照源码:https://docs.djangoproject.com/en/1.8/_modules/django/core/validators/

可以发现,有很多 Validator 类都是类定义的,要使得类产生的对象可以跟函数一样使用,就要定义 __call__ 方法,也就是说在 validator 类里面一定要重写 __call__ 方法,这个方法相当于一个纯粹的函数式 validator 的调用。这样的话,配合在类的成员变量以及 __contruct__ 定义,就可以用 ClassValidator(args) 来建立不同参数下的 validator 实例。

至于 deconstruct 是什么,需要参考 Migration 的文档:https://docs.djangoproject.com/en/1.8/topics/migrations/#adding-a-deconstruct-method

验证器如何工作

在表单里面的工作方式请查看:https://docs.djangoproject.com/en/1.8/ref/forms/validation/

在模型中的工作方式请查看:https://docs.djangoproject.com/en/1.8/ref/models/instances/#validating-objects

插个话,结论就是,在 Model 里面 Validator 会通过 clean_fieldsfull_clean 来调用,而在 django 内核中,默认的 Model.save() 是不自动调用的,具体原因主要是以为兼容旧版的接口,见此问题:http://stackoverflow.com/questions/4441539/why-doesnt-djangos-model-save-call-full-clean,如果要启动这个自动的验证功能,可以考虑安装这个:django smart save 的第三方 app 来修正 https://github.com/danielgatis/django-smart-save

注意在保存模型的时候验证器不会自动起作用,但是如果使用 ModelForm,在 form 里面定义的验证器就都会被执行校验。这可以参考 ModelForm 的文档:https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/,据说是使得对象本身可串行化,使得 migration 时满足一定的条件,看样子这些 validator 是会关联到 migration 里面去的,具体的机制还没看懂,但是不要紧,记得在定义类验证器的时候采取类似如下的语法即可:

@deconstructible
class BaseValidator(object):
    compare = lambda self, a, b: a is not b
    clean = lambda self, x: x
    message = _('Ensure this value is %(limit_value)s (it is %(show_value)s).')
    code = 'limit_value'

    def __init__(self, limit_value, message=None):
        self.limit_value = limit_value
        if message:
            self.message = message

    def __call__(self, value):
        cleaned = self.clean(value)
        params = {'limit_value': self.limit_value, 'show_value': cleaned, 'value': value}
        if self.compare(cleaned, self.limit_value):
            raise ValidationError(self.message, code=self.code, params=params)

内置的验证器

django.core.validators 模块包含了一个可调用的验证器集合用于模型和表单,它们一般都是内部使用的,但你也可以用在你自己的字段中,或者你也可以手动来调用它们,或者放在 field.clean() 这样的手动调用的 clean 方法中使用。

下面就不展开了,可以用的验证器包括:

  • RegexValidator
  • EmailValidator
  • URLValidator
  • MaxValueValidator
  • MinValueValidator
  • MaxLengthValidator
  • MinLengthValidator

【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2015/09/django-1-8-validator.html【Django 1.8 验证器(Validator)】

发表评论

电子邮件地址不会被公开。 必填项已用*标注