How is possible to combine 'excludes' with

2019-08-16 04:13发布

问题:

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

回答1:

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'"]}
  



回答2:

This is just upon understanding your requirement. And this works.

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': 2, 'value_1': 3}]:
    print('Doc: {}'.format(doc))
    n_doc = {}
    if not v.validate(doc, schema):
        print('Error: {}'.format(v.errors))
        n_doc = v.normalized(doc)
        n_doc.update(v.normalized({}))
    else:
        n_doc = v.normalized(doc)
    print('Result: {}'.format(n_doc))

Result:

Doc: {}
Result: {'value_1': 0}
Doc: {'value_2': 1}
Error: {'value_2': ["'value_1' must not be present with 'value_2'"]}
Result: {'value_1': 0, 'value_2': 1}
Doc: {'value_1': 3, 'value_2': 2}
Error: {'value_2': ["'value_1' must not be present with 'value_2'"]}
Result: {'value_1': 0, 'value_2': 2}