Skip to content
guyskk edited this page Mar 29, 2020 · 3 revisions

自定义校验器

validator() 装饰器用于创建自定义的校验器,并使它自动支持 default, optional, desc, invalid_to, invalid_to_default, object 这几个通用参数。

你可以用 acceptoutput 参数控制校验器接受和输出的数据类型:

  • accept (str | object | (str,object)):
    • str: 只接受字符串,将 None 和空字符串都视为 None
    • object: 只接受对象(非字符串)
    • (str,object): (默认值) 接受字符串和对象,将 None 和空字符串都视为 None
  • output (str | object | (str,object)):
    • str: (默认值) 只输出字符串,将 None 转换成空字符串
    • object: 只输出对象(非字符串)
    • (str, object): 输出字符串或对象,通过 object 参数控制输出类型。为避免命名冲突,object 参数在校验器函数签名中会重命名为 output_object

校验器的一般写法:

from validr import T, validator

@validator(accept=(str, object), output=(str, object))
def xxx_validator(compiler, items=None, output_object=False, some_param=None):
    """自定义校验器

    Params:
        compiler: 可以用它编译内部校验器
        items: 可选,只能为标量类型,通过 T.validator(items) 形式的Schema指定
        output_object: 控制是否输出对象, 只在 `output=(str, object)` 时有效
        some_param: 其他参数
    Returns:
        校验函数
    """
    def validate(value):
        """校验函数

        Params:
            value: 待校验的数据
        Returns:
            合法的值或处理后的值
        Raises:
            Invalid: 校验失败
        """
        try:
            # 数据校验和转换
        except Exception:
            # raise Invalid('invalid xxx')
        if output_object:
            # return python object
        else:
            # return string
    return validate

# 使用自定义校验器
compiler = Compiler(validators={
    # 名称: 校验器
    'xxx': xxx_validator,
})

示例,时间间隔校验器:

from validr import T, validator, SchemaError, Invalid, Compiler

UNITS = {'s':1, 'm':60, 'h':60*60, 'd':24*60*60}

def to_seconds(t):
    return int(t[:-1]) * UNITS[t[-1]]

@validator(accept=str, output=object)
def interval_validator(compiler, min='0s', max='365d'):
    """Time interval validator, convert value to seconds

    Supported time units:
        s: seconds, eg: 10s
        m: minutes, eg: 10m
        h: hours, eg: 1h
        d: days, eg: 7d
    """
    try:
        min = to_seconds(min)
    except (IndexError,KeyError,ValueError):
        raise SchemaError('invalid min value') from None
    try:
        max = to_seconds(max)
    except (IndexError,KeyError,ValueError):
        raise SchemaError('invalid max value') from None
    def validate(value):
        try:
            value = to_seconds(value)
        except (IndexError,KeyError,ValueError):
            raise Invalid("invalid interval") from None
        if value < min:
            raise Invalid("interval must >= {} seconds".format(min))
        if value > max:
            raise Invalid("interval must <= {} seconds".format(max))
        return value
    return validate

compiler = Compiler(validators={"interval": interval_validator})
f = compiler.compile(T.interval.max('8h'))
>>> f('15m')
900
>>> f('15x')
...
validr._exception.Invalid: invalid interval, value=15x
>>> f('10h')
...
validr._exception.Invalid: interval must <= 28800 seconds, value=10h
>>> f = compiler.compile(T.interval.default('5m'))
>>> f(None)
300
>>> compiler.compile(T.interval.max('12x'))
...
validr._exception.SchemaError: invalid max value, schema=interval.max('12x')

使用正则表达式构建校验器

from validr import T, Compiler, create_re_validator

regex_time = r'([01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d'
time_validator = create_re_validator("time", regex_time)
compiler = Compiler(validators={"time": time_validator})
f = compiler.compile(T.time.default("00:00:00"))

>>> f("12:00:00")
'12:00:00'
>>> f("12:00:123")
...
validr._exception.Invalid: invalid time, value=12:00:123
>>> f(None)
'00:00:00'
>>>