schema
schema copied to clipboard
Question - How do I define conditional rules?
Say I have a dictionary where the keys are conditional. How do I write a schema to check it?
For example, the following 2 are ok.
{
'name': 'x',
'x_val': 1
}
{
'name': 'y',
'y_val': 1
}
But the following 2 are not
{
'name': 'x',
'y_val': 1
}
{
'name': 'y',
'x_val': 1
}
The existence of what keys need to be in the dict is conditional on the value of name
. I can't just say this is a dict with these keys. Name x
has a certain set of keys (in this case, x_val
), and name y
has a different set of keys (y_val
).
Of course, I can write my own lambda function that takes in such a dictionary and performs the checks. But I was wondering if there's some kind of out of the box solution for this type of validation.
thanks
I'm a new user, so there might be a neater way to take care of conditional rules, but here's an approach based on the Customized Validation section of the readme:
from schema import Schema, SchemaMissingKeyError, Or
class ConditionalSchema(Schema):
def validate(self, data, _is_conditional_schema=True, **kwargs):
data = super().validate(data, _is_conditional_schema=False, **kwargs)
if _is_conditional_schema:
if data["name"] == "x" and "x_val" not in data:
raise SchemaMissingKeyError("x_val")
elif data["name"] == "y" and "y_val" not in data:
raise SchemaMissingKeyError("y_val")
return data
sch = ConditionalSchema(
{
"name": Or("x", "y"),
Or("y_val", "x_val", only_one=True): int,
}
)
sch.validate({"name": "x", "x_val": 1}) # => {'name': 'x', 'x_val': 1}
sch.validate({"name": "y", "y_val": 1}) # => {'name': 'y', 'y_val': 1}
sch.validate({"name": "x", "y_val": 1}) # => schema.SchemaMissingKeyError: x_val
sch.validate({"name": "y", "x_val": 1}) # => schema.SchemaMissingKeyError: y_val
This approach is pretty flexible, but comes at the cost a level of indirection with respect to where the rules are defined.
Note that the passing through of kwargs
is only necessary when nesting different Schema
subclasses and could have been omitted from the above example.
Cheers!