Nesting documents

Documents can contain sub-documents of the same or different types.

To allow the inclusion of sub-documents in your document, add a DocReference to your schema at the position that you expect a sub-document at.

To actually process sub-documents, you need to specify, where they are located with the return value of the subdocuments() method.

Example for a document class Parent that includes an Example document from the previous chapters at either direct or in the map map as values:

from configcrunch import DocReference

class Parent(YamlConfigDocument):
    @classmethod
    def header(cls) -> str:
        return "parent"

    @classmethod
    def schema(cls) -> Schema:
        return Schema({
            'name': str,
            'direct': DocReference(Example),
            'map': {str: DocReference(Example)}
        })

    @classmethod
    def subdocuments(cls):
        return [
            # direct entry processing
            ("direct", Example),
            # dict entry processing (also works for lists)
            ("map[]", Example),
            # If you have more complex documents, you can reference sub fields, by specifying
            # the path to it, seperated by /.
            # ("level0/level1", Example),
            # or ("level0/level1[]", Example),
        ]

The following document would be a valid document for Parent:

# fixtures/parent.yml
parent:
  name: parent
  direct:
    this: is an example-type document
  map:
    one:
      this: is also an example-type document
      int: 3

To load and process this document, load the document like an ordinary document. After that call resolve_and_merge_references() on it. The parameter is not relevant in this case (leave it as an empty list) and will be explained in the next chapter.

document = Parent.from_yaml("fixtures/parent.yml")
document.resolve_and_merge_references([])

You can then validate the document. The sub-documents are validated against their schemas. Validating the document before calling this function will return in a SchemaError.

>>> document.validate()
True

>>> document2 = Parent.from_yaml('fixtures/parent.yml')
>>> # No calling of resolve_and_merge_references
>>> try:
...   document2.validate()
... except SchemaError as err:
...   print(err)  
Key 'map' error:
Key 'one' error:
Expected an instance of 'Example' while validating, got 'dict': {'int': 3, 'this': 'is also an example-type document'}

You can access sub-documents like other fields. Calling freeze on the parent document will also freeze all sub-documents.

>>> document.freeze()
>>> print(document['direct'])
Example({'this': 'is an example-type document'})
>>> print(document['direct']['this'])
is an example-type document
>>> print(document['map']['one']['$name'])  # This will be added to all sub-documents in dicts.
one