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 ` .. 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 "", line 1, in 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 "", line 1, in 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