0

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

dreftymac
  • 31,404
  • 26
  • 119
  • 182
El Ruso
  • 14,745
  • 4
  • 31
  • 54
  • 1
    You cannot use default and at the same time exclude it from other field. You can set default values for missing fields in the document which adds the defaults if its not present. – Reck Mar 10 '18 at 19:07
  • @Reck I think this can be done with the help of [`oneof`](http://docs.python-cerberus.org/en/stable/validation-rules.html#of-rules) but I can not understand how – El Ruso Mar 10 '18 at 22:52
  • 1
    @El Ruse one can obviously not define normalization within the oneof-rule. – funky-future Mar 20 '18 at 13:53
  • @funky-future yep, this is just the case when the overall design of my schema is wrong. – El Ruso Mar 23 '18 at 21:42

2 Answers2

0

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}
Reck
  • 1,388
  • 11
  • 20
  • I want to raise error if `value_1` and `value_2` exists in incoming document, **but** set default `0` to `value_1` if this key not exists in document. In your example document #3 will pass validation, it's not fits to my reqs – El Ruso Mar 10 '18 at 22:47
  • So this is an important information which you need to add in your question in a more descriptive way. – Reck Mar 11 '18 at 04:22
  • first sentence of my question body describe it. :) Your second example works, but completely ruin main goal of use data validation library, you move part of logic outside of validation procedure, it`s definitely not what i want .Tnx for your answer. – El Ruso Mar 11 '18 at 09:24
0

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'"]}
  
dreftymac
  • 31,404
  • 26
  • 119
  • 182
  • Tnx. As I say in comments to question: " this is just the case when the overall design of my schema is wrong" I didn't delete this question only for educational purposes. And now we have answer which can be marked as accepted :) – El Ruso Dec 10 '18 at 02:16