程序员成长园效率人生Python

Python Cerberus

2018-03-25  本文已影响245人  若与

cerberus地狱犬 (Cerberus是一个用于Python的轻量级且可扩展的数据验证库)

前言

文章内容有点多,是自己学习cerberus的记录,原文,由于篇幅有限,源码的解析就没有了,源码不是很难,具有很高的参考价值。感谢rino nakasone的翻译支持.

cerberus
CERBERUS, n. The watch-dog of Hades, whose duty it was to guard the entrance; everybody, sooner or later, had to go there, and nobody wanted to carry off the entrance. 
- Ambrose Bierce, The Devil’s Dictionary

CERBERUS,地狱的守卫犬,他们的职责是守卫入口; 每个人迟早都不得不去那里,没有人想要走出入口。
- Ambrose Bierce,魔鬼的字典

这是对cerberus的描述。不过很形象。Cerberus提供了功能强大但简单轻便的数据验证功能,其设计易于扩展,允许自定义验证。它没有依赖性,并且已经从Python 2.6到3.5,PyPy和PyPy3进行了彻底测试。


概述

你可以定义一个验证的模式并将它传递给Validator类的一个实例:

>>> schema = {'name': {'type': 'string'}}
>>> v = Validator(schema)

然后,您只需调用该模式validate()来验证字典。如果验证成功,True则返回:

>>> document = {'name': 'john doe'}
>>> v.validate(document)
True

安装

这部分文档涵盖了Cerberus的安装。使用任何软件包的第一步是正确安装它。

稳定版本

Cerberus在PyPI上,所以你需要做的是:

$pip install cerberus

开发版本

Cerberus是在GitHub上积极开发的,代码总是可用的。如果你想使用开发版本,有两种方法:你可以让pip拉入开发版本,或者你可以告诉它在git checkout上运行。无论哪种方式,建议使用virtualenv。

在新的virtualenv中获取git checkout并以开发模式运行。

$ git clone http://github.com/pyeve/cerberus.git
Initialized empty Git repository in ~/dev/cerberus.git/
$ cd cerberus
$ virtualenv venv --distribute
New python executable in venv/bin/python
Installing distribute............done.
$ . venv/bin/activate
$ python setup.py install
...
Finished processing dependencies for Cerberus

这将拉入依赖关系并激活git头,作为virtualenv中的当前版本。然后,您只需运行即可更新到最新版本。git pull origin

要获得没有git的开发版本,请改为:

$ mkdir cerberus
$ cd cerberus
$ virtualenv venv --distribute
$ . venv/bin/activate
New python executable in venv/bin/python
Installing distribute............done.
$ pip install git+git://github.com/pyeve/cerberus.git
...
Cleaning up...

完成上面你就完成了!

Cerberus用法

基本用法

您定义一个验证模式并将其传递给Validator该类的一个实例 :

>>> schema = {'name': {'type': 'string'}}
>>> v = Validator(schema)

然后,您只需调用该模式validate()来验证字典。如果验证成功,True则返回:

>>> document = {'name': 'john doe'}
>>> v.validate(document)
True

或者,您可以将字典和模式传递给 validate()方法:

>>> v = Validator()
>>> v.validate(document, schema)
True

如果您的模式在实例的生命周期中发生变化,那么这可能非常方便。

有关验证模式详细信息在验证架构。有关所有受支持规则的详细文档,请参阅验证规则和规范化规则。

与其他验证工具不同,Cerberus在第一个验证问题上不会停止并引发异常。整个文档将始终处理,并且 False如果验证失败将返回。然后您可以访问该 errors属性以获取问题列表。查看 错误和错误处理以了解不同的输出选项。

>>> schema = {'name': {'type': 'string'}, 'age': {'type': 'integer', 'min': 10}}
>>> document = {'name': 'Little Joe', 'age': 5}
>>> v.validate(document, schema)
False
>>> v.errors
{'age': ['min value is 10']}

DocumentError当文档不是映射时引发A.

Validator类及其实例是可调用的,允许使用以下简写语法:

>>> document = {'name': 'john doe'}
>>> v(document)
True

允许未知(Allowing the Unknown)

默认情况下,只允许模式中定义的键:

>>> schema = {'name': {'type': 'string', 'maxlength': 10}}
>>> v.validate({'name': 'john', 'sex': 'M'}, schema)
False
>>> v.errors
{'sex': ['unknown field']}

然而,您可以通过设allow_unknown为允许未知的文档密钥对 True

>>> v.schema = {}
>>> v.allow_unknown = True
>>> v.validate({'name': 'john', 'sex': 'M'})
True

或者你可以设置allow_unknown一个验证模式,在这种情况下,未知字段将被验证:

>>> v.schema = {}
>>> v.allow_unknown = {'type': 'string'}
>>> v.validate({'an_unknown_field': 'john'})
True
>>> v.validate({'an_unknown_field': 1})
False
>>> v.errors
{'an_unknown_field': ['must be of string type']}

allow_unknown 也可以在初始化时设置:

>>> v = Validator({}, allow_unknown=True)
>>> v.validate({'name': 'john', 'sex': 'M'})
True
>>> v.allow_unknown = False
>>> v.validate({'name': 'john', 'sex': 'M'})
False

allow_unknown也可以设置为规则来配置针对模式规则检查的嵌套映射的验证程序:

>>> v = Validator()
>>> v.allow_unknown
False

>>> schema = {
...   'name': {'type': 'string'},
...   'a_dict': {
...     'type': 'dict',
...     'allow_unknown': True,  # this overrides the behaviour for
...     'schema': {             # the validation of this definition
...       'address': {'type': 'string'}
...     }
...   }
... }

>>> v.validate({'name': 'john',
...             'a_dict': {'an_unknown_field': 'is allowed'}},
...            schema)
True

>>> # this fails as allow_unknown is still False for the parent document.
>>> v.validate({'name': 'john',
...             'an_unknown_field': 'is not allowed',
...             'a_dict':{'an_unknown_field': 'is allowed'}},
...            schema)
False

>>> v.errors
{'an_unknown_field': ['unknown field']}

在版本0.9中更改:allow_unknown也可以为嵌套的字典字段设置。

在版本0.8中更改:allow_unknown也可以设置为验证模式。

获取已处理的文档(Fetching Processed Documents)

标准化和强制是在原始文档的副本上执行的,结果文档通过document-property 可用。

>>> v.schema = {'amount': {'type': 'integer', 'coerce': int}}
>>> v.validate({'amount': '1'})
True
>>> v.document
{'amount': 1}

除了document-property 之外,- Validator实例还具有处理文档并获取处理结果的速记方法。

验证方法(validated Method)

有一个包装器方法validated()返回经过验证的文档。如果文档没有验证None返回,除非您调用关键字参数always_return_document 设置为的方法True。对于这样的流程可能很有用:

v = Validator(schema)
valid_documents = [x for x in [v.validated(y) for y in documents]
                   if x is not None]

如果强制可调用或方法引发异常,那么异常将被捕获并且验证失败。

0.9版本中的新功能。

标准化的方法(normalized Method)

同样,该normalized()方法返回文档的规范化副本而不验证它:

>>> schema = {'amount': {'coerce': int}}
>>> document = {'model': 'consumerism', 'amount': '1'}
>>> normalized_document = v.normalized(document, schema)
>>> type(normalized_document['amount'])
<class 'int'>

1.0版中的新功能。

Warnings

通过Python标准库的warnings模块发出警告,例如关于弃用或可能的故障原因。日志模块可以配置为捕获这些logging.captureWarnings()

验证模式(Validation Schemas)

验证模式validation schema是一种映射,通常是一种映射dict。模式key是目标字典中允许的key。模式值表示必须与相应目标值匹配的规则。

schema = {'name': {'type': 'string', 'maxlength': 10}}

在上面的例子中,我们定义了一个只有一个关键字的目标字典,name预期这个关键字不会超过10个字符。类似的东西 会验证,而类似或不会。{'name': 'john doe'}{'name': 'a very long string'}或{'name': 99}

默认情况下,文档中的所有键都是可选的,除非将所需的规则设置为键。

注册 (Registries)

cerberus模块名称空间中有两个默认注册表,您可以在其中存储模式和规则集的定义,然后可以在验证模式中引用它们。您还可以更实例化Registry对象,并将其绑定到 rules_set_registryschema_registry验证程序的。您也可以在初始化时将它们设置为关键字参数。

如果使用注册表特别有趣

>>> from cerberus import schema_registry
>>> schema_registry.add('non-system user',
...                     {'uid': {'min': 1000, 'max': 0xffff}})
>>> schema = {'sender': {'schema': 'non-system user',
...                      'allow_unknown': True},
...           'receiver': {'schema': 'non-system user',
...                        'allow_unknown': True}}
>>> from cerberus import rules_set_registry
>>> rules_set_registry.extend((('boolean', {'type': 'boolean'}),
...                            ('booleans', {'valueschema': 'boolean'})))
>>> schema = {'foo': 'booleans'}

验证(Validation)

验证模式本身在传递给验证程序时进行验证,或者为文档字段设置一组新的规则。SchemaError当遇到无效的验证模式时引发. 请参阅 验证模式以供参考。

但是,请注意,对于低于该级别的所有更改或注册表中使用的定义更改时,都不会触发验证。因此您可以触发验证并捕获异常:

>>> v = Validator({'foo': {'allowed': []}})
>>> v.schema['foo'] = {'allowed': 'strings are no valid constraint for allowed'}
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "cerberus/schema.py", line 99, in __setitem__
    self.validate({key: value})
  File "cerberus/schema.py", line 126, in validate
    self._validate(schema)
  File "cerberus/schema.py", line 141, in _validate
    raise SchemaError(self.schema_validator.errors)
SchemaError: {'foo': {'allowed': 'must be of list type'}}
>>> v.schema['foo']['allowed'] = 'strings are no valid constraint for allowed'
>>> v.schema.validate()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "cerberus/schema.py", line 126, in validate
    self._validate(schema)
  File "cerberus/schema.py", line 141, in _validate
    raise SchemaError(self.schema_validator.errors)
SchemaError: {'foo': {'allowed': 'must be of list type'}}

序列化(Serialization)

Cerberus的模式都有内建的Python类型:dictliststring等。即使用户自定义的验证规则在模式中通过名称调用作为一个字符串。这种设计的一个有用的副作用是可以用多种方式定义模式,例如使用PyYAML

>>> import yaml
>>> schema_text = '''
... name:
...   type: string
... age:
...   type: integer
...   min: 10
... '''
>>> schema = yaml.load(schema_text)
>>> document = {'name': 'Little Joe', 'age': 5}
>>> v.validate(document, schema)
False
>>> v.errors
{'age': ['min value is 10']}

你当然不必使用YAML,你可以使用你最喜欢的序列化器。 json,只要有一个可以产生嵌套的解码器,就 dict可以用它来定义一个模式。

为了填充和回收其中一个注册表,请使用 extend()all

验证规则(Validation Rules)

allow_unknown

在验证映射以设置子文档验证程序的属性时,可以将它与模式规则 结合使用 allow_unknown.

allowed

如果目标值是可迭代的,则其所有成员必须位于允许的值列表中。其他类型的目标值将验证该值是否在该列表中。

>>> v.schema = {'role': {'type': 'list', 'allowed': ['agent', 'client', 'supplier']}}
>>> v.validate({'role': ['agent', 'supplier']})
True

>>> v.validate({'role': ['intern']})
False
>>> v.errors
{'role': ["unallowed values ['intern']"]}

>>> v.schema = {'role': {'type': 'string', 'allowed': ['agent', 'client', 'supplier']}}
>>> v.validate({'role': 'supplier'})
True

>>> v.validate({'role': 'intern'})
False
>>> v.errors
{'role': ['unallowed value intern']}

>>> v.schema = {'a_restricted_integer': {'type': 'integer', 'allowed': [-1, 0, 1]}}
>>> v.validate({'a_restricted_integer': -1})
True

>>> v.validate({'a_restricted_integer': 2})
False
>>> v.errors
{'a_restricted_integer': ['unallowed value 2']}

在版本0.5.1中进行了更改:添加了对int类型的支持。

allof

验证是否所有提供的约束都验证该字段。
0.9版本中的新功能。

anyof

验证是否有任何提供的约束条件验证该字段。
0.9版本中的新功能。

dependencies

如果文档中存在定义的字段,则此规则允许定义单个字段名称,字段名称序列或字段名称映射以及文档中所依赖的允许值序列。

>>> schema = {'field1': {'required': False}, 'field2': {'required': False, 'dependencies': 'field1'}}
>>> document = {'field1': 7}
>>> v.validate(document, schema)
True

>>> document = {'field2': 7}
>>> v.validate(document, schema)
False

>>> v.errors
{'field2': ["field 'field1' is required"]}

当多个字段名称被定义为依赖关系时,所有这些都必须存在才能验证目标字段。

>>> schema = {'field1': {'required': False}, 'field2': {'required': False},
...           'field3': {'required': False, 'dependencies': ['field1', 'field2']}}
>>> document = {'field1': 7, 'field2': 11, 'field3': 13}
>>> v.validate(document, schema)
True

>>> document = {'field2': 11, 'field3': 13}
>>> v.validate(document, schema)
False

>>> v.errors
{'field3': ["field 'field1' is required"]}

当提供一个映射关系时,不仅仅所有依赖项都必须存在,然而它们的所有允许值都必须匹配。

>>> schema = {'field1': {'required': False},
...           'field2': {'required': True, 'dependencies': {'field1': ['one', 'two']}}}

>>> document = {'field1': 'one', 'field2': 7}
>>> v.validate(document, schema)
True

>>> document = {'field1': 'three', 'field2': 7}
>>> v.validate(document, schema)
False
>>> v.errors
{'field2': ["depends on these values: {'field1': ['one', 'two']}"]}

>>> # same as using a dependencies list
>>> document = {'field2': 7}
>>> v.validate(document, schema)
False
>>> v.errors
{'field2': ["depends on these values: {'field1': ['one', 'two']}"]}


>>> # one can also pass a single dependency value
>>> schema = {'field1': {'required': False}, 'field2': {'dependencies': {'field1': 'one'}}}
>>> document = {'field1': 'one', 'field2': 7}
>>> v.validate(document, schema)
True

>>> document = {'field1': 'two', 'field2': 7}
>>> v.validate(document, schema)
False

>>> v.errors
{'field2': ["depends on these values: {'field1': 'one'}"]}

也支持使用.符号声明对子文档字段的依赖关系:

>>> schema = {
...   'test_field': {'dependencies': ['a_dict.foo', 'a_dict.bar']},
...   'a_dict': {
...     'type': 'dict',
...     'schema': {
...       'foo': {'type': 'string'},
...       'bar': {'type': 'string'}
...     }
...   }
... }

>>> document = {'test_field': 'foobar', 'a_dict': {'foo': 'foo'}}
>>> v.validate(document, schema)
False

>>> v.errors
{'test_field': ["field 'a_dict.bar' is required"]}

在处理子文档时,查询字段的问题始于该文档的级别。为了将处理的文档作为根级别进行处理,声明必须以^开头。两个首^^字符(^^)的出现被解释为一个文字,单个^没有特殊含义。

>>> schema = {
...   'test_field': {},
...   'a_dict': {
...     'type': 'dict',
...     'schema': {
...       'foo': {'type': 'string'},
...       'bar': {'type': 'string', 'dependencies': '^test_field'}
...     }
...   }
... }

>>> document = {'a_dict': {'bar': 'bar'}}
>>> v.validate(document, schema)
False

>>> v.errors
{'a_dict': [{'bar': ["field '^test_field' is required"]}]}

注意

如果要扩展点符号的语义,可以 覆盖该_lookup_field() 方法。

注意

该规则的评估不考虑用所需规则定义的任何约束条件。

在版本1.0.2中更改:支持绝对寻址^

在版本0.8.1中更改:支持将子文档字段作为依赖项。

在版本0.8中进行了更改:支持将依赖项作为字典。

0.7版中的新功能。

empty

如果False验证一个可迭代的值将失败,如果它是空的。将它设置为True手动是毫无意义的,因为它的行为就像完全忽略规则一样。

>>> schema = {'name': {'type': 'string', 'empty': False}}
>>> document = {'name': ''}
>>> v.validate(document, schema)
False

>>> v.errors
{'name': ['empty values not allowed']}

新版本0.0.3。

excludes

您可以声明字段以排除其他字段:

>>> v = Validator()
>>> schema = {'this_field': {'type': 'dict',
...                          'excludes': 'that_field'},
...           'that_field': {'type': 'dict',
...                          'excludes': 'this_field'}}
>>> v.validate({'this_field': {}, 'that_field': {}}, schema)
False
>>> v.validate({'this_field': {}}, schema)
True
>>> v.validate({'that_field': {}}, schema)
True
>>> v.validate({}, schema)
True

您可以要求这两个字段构建 排除 或 '':

>>> v = Validator()
>>> schema = {'this_field': {'type': 'dict',
...                          'excludes': 'that_field',
...                          'required': True},
...           'that_field': {'type': 'dict',
...                          'excludes': 'this_field',
...                          'required': True}}
>>> v.validate({'this_field': {}, 'that_field': {}}, schema)
False
>>> v.validate({'this_field': {}}, schema)
True
>>> v.validate({'that_field': {}}, schema)
True
>>> v.validate({}, schema)
False

您也可以传递多个字段以exclude在列表中:

>>> schema  =  { 'this_field' : { 'type' : 'dict' ,
...                          'excludes' : [ 'that_field' , 'bazo_field' ]},
...           'that_field' : { 'type' : ' dict' ,
...                          'excludes' : 'this_field' },
...           'bazo_field' : { 'type' : 'dict' }} 
>>> v 。验证({ 'this_field' : {}, 'bazo_field': {}}, schema )
False

forbidden

相反 允许 此验证,如果值是定义值中的任何一个,但:

>>> schema = {'user': {'forbidden': ['root', 'admin']}}
>>> document = {'user': 'root'}
>>> v.validate(document, schema)
False

1.0版中的新功能。

items

根据必须验证每个索引对应项目的规则序列验证任何迭代项目。如果给定的迭代器的大小与定义匹配,这些项目才会被评估。

>>> schema = {'list_of_values': {'type': 'list', 'items': [{'type': 'string'}, {'type': 'integer'}]}}
>>> document = {'list_of_values': ['hello', 100]}
>>> v.validate(document, schema)
True
>>> document = {'list_of_values': [100, 'hello']}
>>> v.validate(document, schema)
False

请参阅用于处理任意长度类型的shema(list)规则list。

keyschema

映射mapping 的所有键的验证模式。

>>> v.schema = {'a_nullable_integer': {'nullable': True, 'type': 'integer'}, 'an_integer': {'type': 'integer'}}

>>> v.validate({'a_nullable_integer': 3})
True
>>> v.validate({'a_nullable_integer': None})
True

>>> v.validate({'an_integer': 3})
True
>>> v.validate({'an_integer': None})
False
>>> v.errors
{'an_integer': ['null value not allowed']}

0.9版本中的新功能。

版本1.0更改:重命名propertyschemakeyschema

min,max

允许实现比较运算符的任何类型的最小值和最大值。

版本1.0中更改:允许比较任何类型。

在0.7版本改变:增加了对支持floatnumber类型。

minlength,maxlength

MINLENGTH个,最大长度
迭代次数允许的最小和最大长度。

noneof

验证是否没有提供的约束条件验证该字段。有关详细信息,请参阅*of-rules规则。

0.9版本中的新功能。

nullable

如果True字段值可以设置为None。它本质上ignore_none_values是一个Validator实例的属性的功能,但是允许更细粒度的控制直至字段级别。

>>> v.schema = {'a_nullable_integer': {'nullable': True, 'type': 'integer'}, 'an_integer': {'type': 'integer'}}

>>> v.validate({'a_nullable_integer': 3})
True
>>> v.validate({'a_nullable_integer': None})
True

>>> v.validate({'an_integer': 3})
True
>>> v.validate({'an_integer': None})
False
>>> v.errors
{'an_integer': ['null value not allowed']}

在版本0.7中更改:nullable在缺少类型定义的字段上有效。
0.3.0版本的新功能。

of-rules

这些规则允许您列出多组要验证的规则。如果根据前缀逻辑列表验证对集中的领域将被视为有效allanyonenone

allof 验证是否所有提供的约束都验证该字段。
anyof 验证是否有任何提供的约束条件验证该字段。
noneof 验证是否没有提供的约束条件验证该字段。
oneof 验证所提供的约束是否有恰好适用。

例如,要验证属性是介于0到10还是100和110之间的数字,可以执行以下操作:

>>> schema = {'prop1':
...           {'type': 'number',
...            'anyof':
...            [{'min': 0, 'max': 10}, {'min': 100, 'max': 110}]}}

>>> document = {'prop1': 5}
>>> v.validate(document, schema)
True

>>> document = {'prop1': 105}
>>> v.validate(document, schema)
True

>>> document = {'prop1': 55}
>>> v.validate(document, schema)
False
>>> v.errors   
{'prop1': {'anyof': 'no definitions validated', 'definition 1': 'min value is 100', 'definition 0': 'max value is 10'}}

anyof规则通过为列表中的每个项目创建模式的新实例。上述模式等同于创建两个单独的模式:

>>> schema1 = {'prop1': {'type': 'number', 'min':   0, 'max':  10}}
>>> schema2 = {'prop1': {'type': 'number', 'min': 100, 'max': 110}}

>>> document = {'prop1': 5}
>>> v.validate(document, schema1) or v.validate(document, schema2)
True

>>> document = {'prop1': 105}
>>> v.validate(document, schema1) or v.validate(document, schema2)
True

>>> document = {'prop1': 55}
>>> v.validate(document, schema1) or v.validate(document, schema2)
False

0.9版本中的新功能。

of-rules typesaver

您可以使用规则值列表将任何规则与下划线和另一个规则连接起来以保存输入:

{'foo': {'anyof_type': ['string', 'integer']}}
# is equivalent to
{'foo': {'anyof': [{'type': 'string'}, {'type': 'integer'}]}}

因此,您可以使用它来根据多个模式验证文档,而无需执行自己的逻辑:

>>> schemas = [{'department': {'required': True, 'regex': '^IT$'}, 'phone': {'nullable': True}},
...            {'department': {'required': True}, 'phone': {'required': True}}]
>>> emloyee_vldtr = Validator({'employee': {'oneof_schema': schemas, 'type': 'dict'}}, allow_unknown=True)
>>> invalid_employees_phones = []
>>> for employee in employees:
...     if not employee_vldtr.validate(employee):
...         invalid_employees_phones.append(employee)

oneof

验证所提供的约束是否有一个恰好适用。有关详细信息,请参阅*规则。

0.9版本中的新功能。

readonly

如果True该值是只读的。如果此字段出现在目标字典中,则验证将失败。例如,在接收要在发送到数据存储之前要验证的有效载荷时,这非常有用。该字段可能由数据存储提供,但不应写入。

在版本1.0.2中更改:可以与default和一起使用default_setter

regex 正则

如果字段值与提供的正则表达式不匹配,则验证将失败。它只在字符串值上进行测试。

>>> schema = {'email': {'type': 'string', 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'}}
>>> document = {'email': 'john@example.com'}
>>> v.validate(document, schema)
True

>>> document = {'email': 'john_at_example_dot_com'}
>>> v.validate(document, schema)
False

>>> v.errors
{'email': ["value does not match regex '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$'"]}

有关正则表达式语法的详细信息,请参阅标准库的re-module 文档。请注意,您可以将标志设置为表达式的一部分,并(?aiLmsux)在该文档中查找。

0.7版中的新功能。

required

如果True该字段是强制性的。缺失时验证失败,除非validate()在以下情况下调用update=True:

>>> v.schema = {'name': {'required': True, 'type': 'string'}, 'age': {'type': 'integer'}}
    >>> document = {'age': 10}
    >>> v.validate(document)
    False
    >>> v.errors
    {'name': ['required field']}
    
    >>> v.validate(document, update=True)
    True

注意

具有空值的字符串字段仍将被验证,即使 required设置为True。如果您不想接受空值,请参阅空白规则。

注意

此规则的评估不考虑依赖关系规则中定义的任何约束条件。

在版本0.8中更改:检查字段依赖关系。

schema(dict)

如果为其schema定义了一个-rule 的字段具有作为值的映射,则该映射将根据作为约束提供的模式进行验证。

>>> schema = {'a_dict': {'type': 'dict', 'schema': {'address': {'type': 'string'},
    ...                                                 'city': {'type': 'string', 'required': True}}}}
    >>> document = {'a_dict': {'address': 'my address', 'city': 'my town'}}
    >>> v.validate(document, schema)
    True

注意

要验证映射的任意键,请参阅keyschemarespvalueschema用于验证映射的任意值。

schema (list)

如果schema-validation遇到一个arbritrary大小的序列作为值,序列中的所有项目将根据在schema约束条件中提供的规则进行验证 。

>>> schema = {'a_list': {'type': 'list', 'schema': {'type': 'integer'}}}
    >>> document = {'a_list': [3, 4, 5]}
    >>> v.validate(document, schema)
    True

类型上的模式规则list也是定义和验证字典列表的首选方法。

注意

使用这个规则应该伴随着一个规则,像这个例子一样type明确地限制字段为list-type。否则,当映射根据此规则与序列约束进行验证时,可能会出现错误结果。

 >>> schema = {'rows': {'type': 'list',
    ...                    'schema': {'type': 'dict', 'schema': {'sku': {'type': 'string'},
    ...                                                          'price': {'type': 'integer'}}}}}
    >>> document = {'rows': [{'sku': 'KT123', 'price': 100}]}
    >>> v.validate(document, schema)
    True

在版本0.0.3中更改:list任意长度类型的架构规则

type

数据类型允许使用键值。可以是以下名称之一:

类型名称 Python 2类型 Python 3类型
boolean bool bool
binary bytes [1], bytearray bytes, bytearray
date datetime.date datetime.date
datetime datetime.datetime datetime.datetime
dict collections.Mapping collections.abc.Mapping
float float float
integer int, long int
list collections.Sequence,不含。 string collections.abc.Sequence,不含。 string
number floatintlong,不包括。bool floatint,不包括。bool
set set set
string basestring() str

您可以扩展此列表并支持自定义类型。 类型列表可以用来允许不同的值:

>>> v.schema = {'quotes': {'type': ['string', 'list']}}
>>> v.validate({'quotes': 'Hello world!'})
True
>>> v.validate({'quotes': ['Do not disturb my circles!', 'Heureka!']})
True
>>> v.schema = {'quotes': {'type': ['string', 'list'], 'schema': {'type': 'string'}}}
>>> v.validate({'quotes': 'Hello world!'})
True
>>> v.validate({'quotes': [1, 'Heureka!']})
False
>>> v.errors
{'quotes': [{0: ['must be of string type']}]}

注意

尽管该type规则根本不需要设置,但不鼓励将其解除设置,尤其是在使用更复杂的规则(如schema。如果您决定仍然不想设置显式类型,schema则仅将规则应用于实际可以使用规则的值(如dictlist)。此外, schemaCerberus会尝试确定一个list或一个dict类型规则是否更合适,并根据schema规则的外观来推断它。

注意

请注意,类型验证是在大多数其他字段存在于同一字段之前执行的(预先仅考虑可空只读)。在发生类型故障时,字段中的后续验证规则将被跳过,并且验证将在其他字段上继续。这允许在调用其他(标准或自定义)规则时安全地假定字段类型正确。

版本1.0中更改:添加了binary数据类型。

在版本0.9中更改:如果给出类型列表,则键值必须匹配其中的任何一个。

在0.7.1版本中更改:dictlist类型检查,现在有更一般的执行 MappingSequence类型从内置collections模块。这意味着可以使用Cerberus验证与内置dictlist类型设计为相同接口的自定义类型的实例。在检查list/ 时,我们排除了字符串,Sequence因为它在验证情况下几乎确定字符串不是序列的预期数据类型。

版本0.7中更改:添加了set数据类型。

在版本0.6中更改:添加了number数据类型。

在版本0.4.0中进行了更改:类型验证总是首先执行,并在失败时阻止其他字段验证规则。

在版本0.3.0中更改:添加了float数据类型。

validator

通过调用函数或方法来验证值。

一个函数必须像这样实现以下原型:

def validationname(field, value, error):
    if value is invalid:
        error(field, 'error message')

error参数指向调用验证的_error方法。有关如何提交错误,请参阅 扩展Cerberus

下面是一个测试整数是否为奇数的例子:

def oddity(field, value, error):
    if not value & 1:
        error(field, "Must be an odd number")

然后,你可以验证这样一个值:

>>> schema = {'amount': {'validator': oddity}}
>>> v = Validator(schema)
>>> v.validate({'amount': 10})
False
>>> v.errors
{'amount': ['Must be an odd number']}

>>> v.validate({'amount': 9})
True

如果规则的约束是一个字符串,那么该Validator实例必须有一个前缀为该名称的方法_validator_。请参阅 扩展Cerberus以获得与上述基于功能的示例等效的内容。

约束条件也可以是连续调用的一系列条件。

schema = {'field': {'validator': [oddity, 'prime number']}}

valueschema

映射的所有值进行验证模式。

>>> schema = {'numbers': {'type': 'dict', 'valueschema': {'type': 'integer', 'min': 10}}}
>>> document = {'numbers': {'an integer': 10, 'another integer': 100}}
>>> v.validate(document, schema)
True

>>> document = {'numbers': {'an integer': 9}}
>>> v.validate(document, schema)
False

>>> v.errors
{'numbers': [{'an integer': ['min value is 10']}]}

0.7版中的新功能。

在版本0.9中更改:重命名keyschemavalueschema

规范化规则

规范化规则适用于字段,也schema适用于映射,以及通过schema(对于序列)allow_unknownkeyschema和, 定义为批量操作valueschemaanyof不会处理定义中用于测试变体(如with)的规范化规则。

重命名字段

您可以在进一步处理之前定义要重命名的字段。

>>> v = Validator({'foo': {'rename': 'bar'}})
>>> v.normalized({'foo': 0})
{'bar': 0}

要让一个可调用的字段或任意字段重命名,您可以定义一个用于重命名的处理程序。如果约束是一个字符串,则它指向一个 自定义方法。如果约束是可迭代的,则通过该链处理该值。

>>> v = Validator({}, allow_unknown={'rename_handler': int})
>>> v.normalized({'0': 'foo'})
{0: 'foo'}
>>> even_digits = lambda x: '0' + x if len(x) % 2 else x
>>> v = Validator({}, allow_unknown={'rename_handler': [str, even_digits]})
>>> v.normalized({1: 'foo'})
{'01': 'foo'}

1.0版中的新功能。

清除未知字段(Purging Unknown Fields)

重命名后,如果实例的purge_unknown属性为 未知字段,则会清除未知字段 ; 它默认为。您可以在初始化时设置每个关键字参数的属性,也可以将其设置为子文档的规则(请参阅允许未知)。默认是 。ValidatorTrue``False``allow_unknown``False

>>> v = Validator({'foo': {'type': 'string'}}, purge_unknown=True)
>>> v.normalized({'bar': 'foo'})
{}

1.0版中的新功能。

默认值(Default Values)

您可以使用default规则为文档中缺少的字段设置默认值。

>>> v.schema = {'amount': {'type': 'integer'}, 'kind': {'type': 'string', 'default': 'purchase'}}
>>> v.normalized({'amount': 1}) == {'amount': 1, 'kind': 'purchase'}
True

>>> v.normalized({'amount': 1, 'kind': None}) == {'amount': 1, 'kind': 'purchase'}
True

>>> v.normalized({'amount': 1, 'kind': 'other'}) == {'amount': 1, 'kind': 'other'}
True

您还可以定义一个可调用的默认setter来动态设置默认值。使用当前(子)文档作为唯一参数调用可调用函数。可调对象甚至可以相互依赖,但如果存在无法解析/循环依赖的情况,则标准化将失败。如果约束是一个字符串,则它指向一个自定义方法

>>> v.schema = {'a': {'type': 'integer'}, 'b': {'type': 'integer', 'default_setter': lambda doc: doc['a'] + 1}}
>>> v.normalized({'a': 1}) == {'a': 1, 'b': 2}
True

>>> v.schema = {'a': {'type': 'integer', 'default_setter': lambda doc: doc['not_there']}}
>>> v.normalized({})
>>> v.errors
{'a': ["default value for 'a' cannot be set: Circular dependencies of default setters."]} 

你甚至可以在同一个字段上同时使用default只读。这将创建一个无法手动赋值的字段,但它将由Cerberus自动提供默认值。当然同样适用default_setter

在版本1.0.2中更改:可以与只读一起使用。

1.0版中的新功能。

值强制(Value Coercion)

强制允许您在验证文档之前将可调用对象(作为对象或自定义标准化方法的名称给定 )应用于值。可调用的返回值将替换文档中的新值。这可以用来转换值或在验证数据之前对数据进行清理。如果约束是可迭代的,则通过该链处理该值。

>>> v.schema = {'amount': {'type': 'integer'}}
>>> v.validate({'amount': '1'})
False

>>> v.schema = {'amount': {'type': 'integer', 'coerce': int}}
>>> v.validate({'amount': '1'})
True
>>> v.document
{'amount': 1}

>>> to_bool = lambda v: v.lower() in ['true', '1']
>>> v.schema = {'flag': {'type': 'boolean', 'coerce': to_bool}}
>>> v.validate({'flag': 'true'})
True
>>> v.document
{'flag': True}

0.9版本中的新功能。

错误和错误处理

错误可以通过Python接口进行评估,或者通过错误处理程序处理为不同的输出格式。

错误处理程序

处理errors文档后,错误处理程序将通过验证程序的属性返回不同的输出 。要使用的错误处理程序可以作为关键字参数传递 error_handler给验证程序的初始化,或者在任何时候通过设置其属性具有相同的名称。在初始化时,可以提供一个实例或一个类。要将带有关键字参数的字典传递给类的初始化,请提供一个包含错误处理程序和字典的二值元组。

以下处理程序可用:

  • BasicErrorHandler:这是返回字典的默认值。键是指文档的键,值是包含错误消息的列表。嵌套字段的错误作为这些列表的最后一项保存在字典中。

Python接口

错误表示为ValidationError具有以下属性:

  • document_path:文档中的路径。对于扁平字典,这只是元组中键的名称,对于嵌套元素,它全部遍历键名。序列中的项目由其索引表示。
  • schema_path:架构内的路径。
  • code:错误的唯一标识符。查看错误代码列表。
  • rule:发生错误时评估的规则。
  • constraint:该规则的约束。
  • value:正在验证的值。
  • info:此元组包含与错误一起提交的其他信息。对于大多数错误,这实际上是没有的 对于批量验证(例如使用itemskeyschema)此属性保留所有单个错误。查看源代码中规则的执行情况,以确定其额外的日志记录。

Validator 处理文档后,您可以访问每个实例属性的错误:

  • _errors:该列表包含所有提交的错误。它不打算通过这个属性直接操作错误。您可以测试是否至少有一个具有特定错误定义的错误是in该列表。
  • document_error_treedict类似于A 的对象,允许您查询与您的文档相对应的节点。该节点的错误包含在它的errors属性中,您可以测试该属性_errors并在遍历节点时放弃。如果节点或更低节点中没有发生错误,None则会返回。
  • schema_error_tree:与使用的模式类似。

在版本1.0中更改:错误存储ValidationError在a中 ErrorList

Examples

>>> schema = {'cats': {'type': 'integer'}}
>>> document = {'cats': 'two'}
>>> v.validate(document, schema)
False
>>> cerberus.errors.BAD_TYPE in v._errors
True
>>> v.document_error_tree['cats'].errors == v.schema_error_tree['cats']['type'].errors
True
>>> error = v.document_error_tree['cats'].errors[0]
>>> error.document_path
('cats',)
>>> error.schema_path
('cats', 'type')
>>> error.rule
'type'
>>> error.constraint
'integer'
>>> error.value
'two'

Cerberus扩展

虽然你可以与一起使用的功能coercevalidator规则,你可以很容易地扩展Validator 与自定义类的规则,类型,验证,coercers和 default_setters。虽然基于功能的风格更适合特殊用途和一次性用途,但自定义类可以利用这些可能性:

  • 自定义规则可以用模式中的约束来定义
  • 扩展可用类型小号
  • 使用额外的上下文数据
  • 模式是可序列化的

模式中对这些自定义方法的引用可以使用空格字符而不是下划线,例如是一个别名。{'foo':{'validator': 'is odd'}}``{'foo': {'validator': 'is_odd'}}

自定义规则

假设在我们的用例中,一些值只能表示为奇数整数,因此我们决定在isodd验证模式中添加对新规则的支持:

schema  =  { 'amount' : { 'isodd' : True , 'type' : 'integer' }}

这是我们将如何去实现的:

from cerberus import Validator

class MyValidator(Validator):
    def _validate_isodd(self, isodd, field, value):
        """ Test the oddity of a value.

        The rule's arguments are validated against this schema:
        {'type': 'boolean'}
        """
        if isodd and not bool(value & 1):
            self._error(field, "Must be an odd number")

通过继承Cerberus Validator类并添加自定义 _validate_<rulename>方法,我们只是增强了Cerberus以满足我们的需求。自定义规则isodd现在在我们的模式中可用,而真正重要的是,我们可以使用它来验证所有奇数值:

>>> v = MyValidator(schema)
>>> v.validate({'amount': 10})
False
>>> v.errors
{'amount': ['Must be an odd number']}
>>> v.validate({'amount': 9})
True

由于模式本身已经过验证,因此可以在规则的实现方法的文档字符串中将约束条件作为文字Python表达式来验证该规则的模式中给出的参数。文档字符串只包含字面值,或者文字字符位于文档字符串的底部,后面是 更多示例, 参见贡献规则的来源。The rule's arguments are validated againstthis schema:

自定义数据类型

Cerberus支持并验证多种标准数据类型(请参见类型)。在构建自定义验证器时,您可以添加和验证自己的数据类型。

例如,Eve(快速构建和部署RESTful Web服务的工具)支持自定义objectid类型,用于验证字段值是否符合BSON / MongoDB ObjectId 格式。

通过向_validate_type_<typename>自己的Validator 子类添加一个方法来扩展支持的数据类型集。这段代码直接来自Eve来源,显示了如何objectid 实现:

def _validate_type_objectid(self, value):
    """ Enables validation for `objectid` schema attribute.
    :param value: field value.
    """
    if re.match('[a-f0-9]{24}', value):
        return True

新版本0.0.2。

版本1.0中更改:类型验证逻辑已更改,请参阅升级到Cerberus 1.0

自定义验证器

如果验证测试不依赖于指定的约束,那么可以将这些验证器定义为规则而不是规则。当validator规则被赋予一个字符串作为约束时,它们被调用 。带有前缀的匹配方法_validator_将以field和value作为参数进行调用:

def _validator_oddity(self, field, value):
    if not value & 1:
        self._error(field, "Must be an odd number")

自定义Coercers

您还可以定义返回coerced值或指向方法的自定义方法rename_handler。方法名称必须以前缀_normalize_coerce_

class MyNormalizer(Validator):
    def __init__(self, multiplier, *args, **kwargs):
        super(MyNormalizer, self).__init__(*args, **kwargs)
        self.multiplier = multiplier

    def _normalize_coerce_multiply(self, value):
        return value * self.multiplier
>>> schema = {'foo': {'coerce': 'multiply'}}
>>> document = {'foo': 2}
>>> MyNormalizer(2).normalized(document, schema)
{'foo': 4}

自定义默认设置器

与自定义重命名处理程序类似,也可以创建自定义默认设置程序。

from datetime import datetime

class MyNormalizer(Validator):
    def _normalize_default_setter_utcnow(self, document):
        return datetime.utcnow()
>>> schema = {'creation_date': {'type': 'datetime', 'default_setter': 'utcnow'}}
>>> MyNormalizer().normalized({}, schema)
{'creation_date': datetime.datetime(...)}

Limitations

覆盖特定的贡献规则可能是一个糟糕的主意。

实例化自定义验证器

要在子类中使用其他上下文信息 Validator,请使用如下模式:

class MyValidator(Validator):
    def __init__(self, *args, **kwargs):
        if 'additional_context' in kwargs:
            self.additional_context = kwargs['additional_context']
        super(MyValidator, self).__init__(*args, **kwargs)

    # alternatively define a property
    @property
    def additional_context(self):
        return self._config.get('additional_context', 'bar')

    def _validate_type_foo(self, field, value):
        make_use_of(self.additional_context)

这确保了额外的上下文将Validator在验证期间可能使用的子实例中可用 。

0.9版本中的新功能。

有一个函数validator_factory()可以获得Validator具有连接文档字符串的 突变体。

1.0版中的新功能。

相关的验证器 -属性

Validator在编写自定义验证器时,应该注意一些属性。

Validator.document

验证器document在获取验证字段时访问属性。它还允许验证字段在文档的其余部分发生。

0.7.1版本的新功能。

Validator.schema

同样,该schema属性保存使用的模式。

注意

该属性与schema在某个时刻传递给验证器的对象不同。此外,其内容可能会有所不同,尽管它仍然代表着最初的限制。它提供了与a相同的界面 dict

Validator._error

有三个签名被接受用于向Validator错误隐藏提交错误 。如有必要,给定的信息将被解析为一个新的实例ValidationError

完全公开

为了能够在以后获得对错误上下文的全面了解,您需要_error()使用两个强制参数进行调用:

  • 发生错误的字段
  • a的一个实例 ErrorDefinition

对于自定义规则,您需要定义一个错误,如同ErrorDefinition一个唯一的ID和违反的原因规则。请参阅errors 提供的错误定义列表。请记住,第7位标记为组错误,第5位标记由对不同规则集进行验证而引发的错误。

或者,您可以提交更多参数作为信息。用于人类目标的错误处理程序将使用这些作为格式化消息时的位置参数str.format()。序列化处理程序将把这些值保存在一个列表中。

1.0版中的新功能。

简单的自定义错误

一个更简单的形式是_error()用字段和字符串作为消息来调用。然而,由此产生的错误将不包含违反约束的信息。这应该保持向后兼容性,但也可以在不需要深入错误处理时使用。

多重错误

使用儿童验证器时,提交所有错误是一种方便; 这是一个ValidationError实例列表。

1.0版中的新功能。

Validator._get_child_validator

如果您需要Validator-subclass的 _get_child_validator()另一个实例,则-method会返回另一个以与之相同的参数启动的实例self。您可以指定重写关键字参数。由于属性document_pathschema_path(见下文)由子验证器继承,所以可以通过将关键字document_crumb和 值传递给单个值或值元组来扩展这些属性schema_crumb。研究示例用法的源代码。

0.9版本中的新功能。

在版本1.0中更改:添加document_crumbschema_crumb作为可选的关键字参数。

Validator.root_document,.root_schema&root_allow_unknown

子验证程序 - 用于验证schema- 时可以访问正在处理的第一代验证程序的文档和模式以及通过它root_documentroot_schema root_allow_unknown属性访问未知字段的约束。

1.0版中的新功能。

Validator.document_path&Validator.schema_path

这些属性分别维护文档中的键路径以及可能的父验证程序遍历的模式。提交错误时,两者都将用作基本路径。

1.0版中的新功能。

Validator.recent_error

通过recent_error-attribute 可以访问最后一次提交的单个错误 。

1.0版中的新功能。

Validator.mandatory_validations&Validator.priority_validations

如果要调整每个字段验证的验证逻辑,则可以覆盖这些类属性。 mandatory_validations是一个包含将针对每个字段进行验证的规则的元组,无论规则是否定义为模式中的字段。priority_validations是有序规则的元组,将在其他任何规则之前进行验证。如果验证方法或函数返回True,则不会为该字段考虑进一步的规则。

1.0版中的新功能。

API文档

Validator Class

Rules Set & Schema Registry

Error Handlers

Python Error Representations

Error Codes

These errors are used as code.

Exceptions

Utilities

Schema Validation Schema

Against this schema validation schemas given to a vanilla Validator will be validated:

上一篇下一篇

猜你喜欢

热点阅读