New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple anonymous schema validation #132

Closed
calve opened this Issue Jul 21, 2015 · 6 comments

Comments

Projects
None yet
2 participants
@calve
Copy link
Contributor

calve commented Jul 21, 2015

Hi all,

I am looking for a schema that can validates multiples schema ;

document1 = {
   "this_field" : {"bar_field" : "value"},
   "common_field1" : 42,
   "common_field2" : "hello",
}
document2 = {
   "that_field" : "blop",
   "common_field1" : 42,
   "common_field2" : "hello",
}

(note the difference between this_field and that_field)

From what I understand from the documentation, it could be written using *-of :

schema = {
    "oneof_schema":[
        {"this_field" : {"type": "dict"}},
        {"that_field": {"type" : "string"}}
    ],
   "common_field1" : {"type" : "integer"},
   "common_field2" : {"type" : "string"}
}

When I try to validate, I get the following error

>>> Validator().validate(document1, schema) 
 File "/home/g/.virtualenvs/p/lib/python2.7/site-packages/cerberus/cerberus.py", line 231, in validate
    return self._validate(document, schema, update=update, context=context)
  File "/home/g/.virtualenvs/p/lib/python2.7/site-packages/cerberus/cerberus.py", line 249, in _validate
    self.validate_schema(schema)
  File "/home/g/.virtualenvs/p/lib/python2.7/site-packages/cerberus/cerberus.py", line 397, in validate_schema
    raise SchemaError(errors.ERROR_DEFINITION_FORMAT.format(field))

cerberus.cerberus.SchemaError: unknown rule 'oneof_schema' for field 'oneof_schema'

I need to "name" the field used by oneof to pass this error

schema = {
    "named" : {
        "type" : "dict",
        "oneof": [
            {"schema": {"this_field" : {"type": "dict"}}},
            {"schema": {"that_field": {"type" : "string"}}}
        ]
    },
   "common_field1" : {"type" : "integer"},
   "common_field2" : {"type" : "string"}
}

but this won't validate my original document1 and document2, which now needs to contains a named key

document3 = {
   "named" : {"this_field" : {"foo":42}},
   "common_field1" : 34
}

document4 = {
   "named" : {"that_field" : "test"},
   "common_field1" : 34
}

Correctly outputs

>>> Validator().validate(document3, schema)
True
>>> Validator().validate(document4, schema)
True

But this is not as good as what I need since it changes the structures of my documents (document1 != document3 and document2 != document4)

How can I write a schema that validates both document1 and document2, without using a specific named key ?

Please, let me know if I did not made myself clear, or if you need any extra more information.

@funkyfuture

This comment has been minimized.

Copy link
Member

funkyfuture commented Jul 21, 2015

unfortunately such convenience isn't possible.

apparently your first schema will not work because constraints are always bound to a field, so 'oneof_schema' will be interpreted like this.

the second schema is a good approach to deal with it, but the 'common' fields are kept one level too high.

i would recommend you to write write (parts of) the schema without shortcuts and then figure out how it can be condensed.

i hope that helps you. i'm not testing your bits, so i may oversee something.

@funkyfuture

This comment has been minimized.

Copy link
Member

funkyfuture commented Jul 21, 2015

ah, of course a contribution to the documentation that will hinder others from getting confused too about it will be highly appreciated.

@calve

This comment has been minimized.

Copy link
Contributor

calve commented Jul 29, 2015

I'm afraid I did not understand all you said about constraints and field. I would be happy to contribute to the documentation, but I don't wont to mess with it.

Anyway, I managed to solve my problem with the following class based custom validator :

def _validate_exclude(self, exclude, field, value):
        if exclude in self._current:
            self._error(field, "Exclude !")

And my schema now looks like :

schema = {
    "this_field" : {
        "type": "dict",
        "exclude" : "that_field"
    },
    "that_field": {
         "type" : "string",
         "exclude": "this_field",
   },
   "common_field1" : {"type" : "integer"},
   "common_field2" : {"type" : "string"}
}

I am totally happy with this solutions that allow me to define mutually exclusive (xor) fields in cerberus. Hope it helps another soul on the internet.

@calve calve closed this Jul 29, 2015

@funkyfuture

This comment has been minimized.

Copy link
Member

funkyfuture commented Jul 29, 2015

be aware the self._current is no more available after refactoring. it's just self.document now.

so, could an implementation #84 have been a more straight-forward help for you?

@calve

This comment has been minimized.

Copy link
Contributor

calve commented Jul 29, 2015

Good to know about self.document. Thanks.

I had looked at #84 before opening this issue. Actually, my main problem was to have exclusive field at top level.

#84 would have been more straight-forward. Didn't I just implemented it here ?

@funkyfuture

This comment has been minimized.

Copy link
Member

funkyfuture commented Jul 29, 2015

well, in a rudimentary way you did it. feel free to open a pull request with a proper implementation that includes:

  • validation of the rule
  • rename it to the less ambigious excludes as proposed
  • tests
  • feature documentation
  • handling of multiple excluded fields in a list
  • dismissing of required-rules for fields that have been effectively excluded
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment