Optional/required

Unlike with querying values you can, but don't need to specify whether elements queried from the document are optional or required. As you will most often use the elements' values themselves, the necessity for the element hierarchy in between the document and that value is automatically inferred by whether the value itself is optional or required:

document.section('content').section('translated').field('revision').optional_string_value()

The line above can be run for an entirely empty document and still return None, while by modifying the query of the value itself to be required would make the code raise an error for the first element missing in that hierarchy instead (again assuming an empty document):

document.section('content').section('translated').field('revision').required_string_value()

This would produce an error:

The section 'content' is missing - in case it has been specified look for typos and also check for correct capitalization.

   Line | Content
 *    1 | 

With that said, there will of course also be cases where you want to be explicit about whether an element is optional or required, for this you can use the explicit optional_* or required_* accessor variant, which exists for all element types, for instance:

document.required_field('author')
document.optional_section('notes')

As with value accessors, the optional_* variants return None if the element does not exist (be aware that you should not chain further queries after such a call because you'd potentially be calling a function on None), while the required_* variants immediately raise an error when the element is missing (here you can safely chain further queries afterwards).

Below is a pattern you might find useful, it's basically saying "There always has to be a field with the key 'author', but it can be empty". It returns None or the value, and only raises an error when the field itself is missing.

document.required_field('author').optional_string_value()

The pattern below is sort of the reverse, it's "If there is a field with the key 'author', it also requires a value", and guarantees there is a value if there is also a field, otherwise an error is raised.

author = document.optional_field('author')

if author:
  value = author.required_string_value()
  # ...

Last but not least a pattern that does not work:

author = document.field('author')

if author:
  # Will always enter here, author is never None

As shown earlier you can safely and deeply query a non-existing document hierarchy. This is possible because the standard query methods return proxy objects (e.g. MissingSection) instead of None when an element in the chain is missing, and with that it's clear why the previous example does not work, instead you have to use the explicit optional_field accessor to ensure you are being returned None if there is no element.

author = document.optional_field('author')

if author:
  # This works, author can be None now

Next page: Dynamic layouts