Cerberus: Can the schema copy a value to multiple fields?

243 views Asked by At

I want to take an input document like the below and copy the 'foo' key to multiple fields with different coercions and validations to get something like this:

>>> input_doc = {'foo': 10}
>>> coercions = {'foo': {'copy_to': 'bar', 'coerce': str}, 'bar': {'coerce': add_five}}
>>> v = Validator(coercions)
>>> v.normalized(input_doc)
{'foo': '10', 'bar': 15}

I know I could copy the values to other keys before feeding into Cerberus, but if I could do it all in the schema that would be ideal.

Cerberus has the 'rename' normalization rule, which runs before other coercions and validations, but if you pass it a container it just renames the key to that container rather than copying to each.

I think a custom rule could handle it except that it runs too late in the process. I need to copy pre-validation and even pre-coercion, ideally.

Maybe I'm asking too much of Cerberus, but it's so close to being a one-stop solution for my data munging needs.

1

There are 1 answers

0
Douglas Storz On BEST ANSWER

The below works but it's a bit hacky. Still wondering if there's a better way to handle this.

Basically, I made a custom Validator class and give it a custom rename_handler method so that it has direct access to the document. The KEY_MAP is a dict that tells the Validator which field names to copy where, since you can't pass arguments to a rename_handler.

from cerberus import Validator

class CustomValidator(Validator):
    KEY_MAP = {'foo': 'bar'}

    def _normalize_coerce_copy_field(self, field):
        copy = self.KEY_MAP[field]
        self.document[copy] = self.document[field]

        return field


>>> input_doc = {'foo': 10}
>>> coercions = {'foo': {'rename_handler': 'copy_field', 'coerce': str}, 'bar': {'coerce': lambda x: x + 5}}
>>> v = CustomValidator(coercions)
>>> v.normalized(input_doc)
{'foo': '10', 'bar': 15}