Search code examples
pythonvalidationcerberus

cerberus. at least one of two keys should be present is json


I'm using Cerberus to validate the data posted as JSON to a Flask-based ReST-API. I want at least one of the two fields freight_id and tender_id to be present.

These mappings would be considered as valid:

{"freight_id": 1, "price" : 12000}

{"tender_id": 1, "price": 12000}

{"freight_id": 1, "tender_id" : 2, "price": 12000}

While this one would not:

{"price": 12000}

How can I formulate a schema for such validation with Cerberus?

I almost read all documentation, but did not find any answer. The excludes-rule doesn't fit my need.


Solution

  • Using cerberus 1.0, you can implement this using oneof rule in its agglutinating form as in this documentation example. With this, you can validate against different schemas of which exactly one must validate:

    The downside is that you may need an additional level on you dict, like price below:

    First schema, freight and price:

    >>> schema_1 = {'freight_id': {'type': 'integer', 'required': True},
    ...             'price': {'type': 'integer', 'required': True}}
    

    Second schema, tender and price:

    >>> schema_2 = {'tender_id': {'type': 'integer', 'required': True},
    ...             'price': {'type': 'integer', 'required': True}}
    

    Third schema, freight, tender and price:

    >>> schema_3 = {'tender_id': {'type': 'integer', 'required': True},
    ...             'freight_id': {'type': 'integer', 'required': True},
    ...             'price': {'type': 'integer', 'required': True}}
    

    Putting these together:

    >>> from cerberus import Validator
    >>>
    >>> price_validator = Validator(
    ...     {'price': {'type': 'dict', 
    ...                'oneof_schema': [schema_1, schema_2, schema_3]}})
    

    The results:

    >>> price_validator.validate({"price": {"freight_id": 1, "price" : 12000}}) 
    True
    >>> price_validator.validate({"price": {"tender_id": 2, "price" : 12000}})
    True
    >>> price_validator.validate(
    ...     {"price": {"freight_id": 1, "tender_id": 2, "price": 1200}}) 
    True
    
    >>> price_validator.validate({"price": {"freight_id": 1, "tender_id": 2}}) 
    False 
    >>> price_validator.validate({"price": {"price" : 12000}})
    False