1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
Validation Schemas
==================
A validation schema is a :term:`mapping`, usually a :class:`dict`. Schema keys
are the keys allowed in the target dictionary. Schema values express the rules
that must be matched by the corresponding target values.
.. testcode::
schema = {'name': {'type': 'string', 'maxlength': 10}}
In the example above we define a target dictionary with only one key, ``name``,
which is expected to be a string not longer than 10 characters. Something like
``{'name': 'john doe'}`` would validate, while something like ``{'name': 'a
very long string'}`` or ``{'name': 99}`` would not.
By default all keys in a document are optional unless the :ref:`required`-rule
is set ``True`` for individual fields or the validator's :attr:~cerberus.Validator.require_all
is set to ``True`` in order to expect all schema-defined fields to be present in the document.
Registries
----------
There are two default registries in the cerberus module namespace where you can
store definitions for schemas and rules sets which then can be referenced in a
validation schema. You can furthermore instantiate more
:class:`~cerberus.Registry` objects and bind them to the
:attr:`~cerberus.Validator.rules_set_registry` or
:attr:`~cerberus.Validator.schema_registry` of a validator. You may also set
these as keyword-arguments upon intitialization.
Using registries is particularly interesting if
- schemas shall include references to themselves, vulgo: schema recursion
- schemas contain a lot of reused parts and are supposed to be
:ref:`serialized <schema-serialization>`
.. doctest::
>>> 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}}
.. doctest::
>>> from cerberus import rules_set_registry
>>> rules_set_registry.extend((('boolean', {'type': 'boolean'}),
... ('booleans', {'valuesrules': 'boolean'})))
>>> schema = {'foo': 'booleans'}
Validation
----------
Validation schemas themselves are validated when passed to the validator or a
new set of rules is set for a document's field. A :exc:`~cerberus.SchemaError`
is raised when an invalid validation schema is encountered. See
:ref:`schema-validation-schema` for a reference.
However, be aware that no validation can be triggered for all changes below
that level or when a used definition in a registry changes. You could therefore
trigger a validation and catch the exception:
>>> v = Validator({'foo': {'allowed': []}})
>>> v.schema['foo'] = {'allowed': 1}
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 container 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 container type'}}
.. _schema-serialization:
Serialization
-------------
Cerberus schemas are built with vanilla Python types: ``dict``, ``list``,
``string``, etc. Even user-defined validation rules are invoked in the schema
by name as a string. A useful side effect of this design is that schemas can
be defined in a number of ways, for example with PyYAML_.
.. doctest::
>>> import yaml
>>> schema_text = '''
... name:
... type: string
... age:
... type: integer
... min: 10
... '''
>>> schema = yaml.safe_load(schema_text)
>>> document = {'name': 'Little Joe', 'age': 5}
>>> v.validate(document, schema)
False
>>> v.errors
{'age': ['min value is 10']}
You don't have to use YAML of course, you can use your favorite serializer.
:mod:`json` for example. As long as there is a decoder that can produce a nested
``dict``, you can use it to define a schema.
For populating and dumping one of the registries, use
:meth:`~cerberus.Registry.extend` and :meth:`~cerberus.Registry.all`.
.. _PyYAML: https://pyyaml.org
|