Search code examples
pythonvalidationcerberus

How is possible to combine 'excludes' with 'default' in schema?


field_1 must be 0 by default, but not allowed with field_2. My try:

from cerberus import Validator

schema = {
    'value_1': {
        'type': 'integer',
        'default': 0
    },
    'value_2': {
        'type': 'integer',
        'excludes': ['value_1', ]
    }
}
v = Validator(schema)

for doc in [{}, {'value_2': 1}, {'value_2': 1, 'value_2': 1}]:
    if not v.validate(doc, schema):
        print(v.errors)
    else:
        print(v.normalized(doc))

I got:

{'value_1': 0}
{'value_2': ["'value_1' must not be present with 'value_2'"]}
{'value_2': ["'value_1' must not be present with 'value_2'"]}

I want to validate second document without errors with normalized result {'value_1': 0, 'value_2': 1}. How can I achieve the desired result?

EDIT More clear explanation of my goals:
- I want to raise error if value_1 and value_2 exists in incoming document, but set 0 to value_1 if this key not exists in document.
- I want to do it inside cerberus validation/normalization procedure and want to solve it by changing validation schema or validator


Solution

  • Quick Answer (TL;DR)

    • validation and normalization can always be separated into distinct steps

    Detailed Answer

    Context

    • python 2.7
    • cerberus data-structure validation and normalization tool

    Problem

    • Scenario: Developer ElRusoDevoWoze wishes to combine validation with data normalization, in order to provide default values for missing fields.

    Solution

    • separate data validation from data normalization

    Rationale

    • rationale ;; validation and normalization can be thought of as separate processes
      • proc1 ;; distinguish unacceptable inputs from acceptable inputs
        • (garbage vs treasure)
        • (authenticated vs unauthenticated)
        • (well-formed vs non-well-formed)
      • proc2 ;; optimize the content of acceptable inputs

    Example

    • The following example creates and applies two schemas
    • One schema provides the default values, the other does the validation

      import pprint
      import yaml
      from cerberus import Validator
      pass
      
      schema_vali = yaml.safe_load('''
        value_1:
          type:       integer
          excludes:   value_2
          required:   True
        value_2:
          type:       integer
          excludes:   value_1
          required:   True
      ''')
      pass
      
      schema_norm = yaml.safe_load('''
        value_1:
          default:    0
      ''')
      pass
      
      sample_docs = yaml.safe_load('''
        ¯ {}                            ## doc0
        ¯ {'value_1': 1}                ## doc1
        ¯ {'value_2': 1}                ## doc2
        ¯ {'value_1': 1, 'value_2': 1}  ## doc3
        ''')
      pass
      
      vccvali = Validator(schema_vali)
      vccnorm = Validator(schema_norm)
      pass
      
      for ijj,doc in enumerate(sample_docs):
        if vccnorm.validate(doc):
          print("{ijj} NORM-YESS! -->".format(ijj=ijj)),
          print(vccnorm.normalized(doc))
          doc = vccnorm.normalized(doc)
        if not vccvali.validate(doc):
          print("{ijj} VALI-NOPE! -->".format(ijj=ijj)),
          print(vccvali.errors)
        else:
          print("{ijj} VALI-YESS! -->".format(ijj=ijj)),
          print(vccvali.normalized(doc))
          doc = vccnorm.normalized(doc)
      pass
      

    Output result

      0 NORM-YESS! --> {'value_1': 0}
      0 VALI-YESS! --> {'value_1': 0}
      1 NORM-YESS! --> {'value_1': 1}
      1 VALI-YESS! --> {'value_1': 1}
      2 VALI-YESS! --> {'value_2': 1}
      3 VALI-NOPE! --> {'value_1': ["'value_2' must not be present with 'value_1'"], 'value_2': ["'value_1' must not be present with 'value_2'"]}