I am starting with Marshmallow and I want to use it to validate my data (coming from an api request). I have created the following Schemas:
from marshmallow import fields, Schema
from marshmallow.validate import Range
class RequestTopSchema(Schema):
upperTarget = fields.Float(required=True, validate = validate.Range(min = 0.0, max = 80.0))
lowerTarget = fields.Float(required=True)
data = fields.Nested("Values", many = True, required = False)
class Values(Schema):
timestamp = fields.String(required=True)
value = fields.Float(required =True, validate = Range(min = 0.0, max = 100.0))
request_top_schema = RequestTopSchema()
Then I am passing in the following data:
request_data = {
"lowerTarget": 10,
"upperTarget": 20,
"data": [
{
"timestamp": "2020-01-01 11:20:00+02:00",
"value": 156.7
},
{
"timestamp": "2020-01-01 11:25:00+02:00",
"value": "46.7"
}
]
}
request_top_schema.validate(request_data)
and receive:
{}
However, since my first value is above 100 and my second value is in a string format - I would expect this to return two errors.
On the top level, at least some things work - if I pass in:
request_data = {
"lowerTarget": "20",
"upperTarget": 180,
"data": [
{
"timestamp": "2020-01-01 11:20:00+02:00",
"value": 156.7
},
{
"timestamp": "2020-01-01 11:25:00+02:00",
"value": "46.7"
}
]
}
request_top_schema.validate(request_data)
I receive: {'upperTarget': ['Must be greater than or equal to 0.0 and less than or equal to 100.0.']}
So I do receive a validation error for out-of-range, however, my lowerTarget being a string is not failing.
UPDATE - I have managed to solve the problem. The issue was passing in the nested schemas as string. If I change them to objects, it works as intended.
However one issue remained: fields.Float() accepts floats as string - like "100.0". Which seems to be a known issue: https://github.com/marshmallow-code/marshmallow/pull/755 Current solution is using a custom float field.
class Float(fields.Float):
def _deserialize(self, value, attr, data, **kwargs):
if isinstance(value, float) or isinstance(value, int):
return value
else:
raise ValidationError('Field should be int or float')
class RequestTopSchema(Schema):
upperTarget = Float(required=True, validate = validate.Range(min = 0.0, max = 80.0))
lowerTarget = Float(required=True)
data = fields.Nested(Values, many = True, required = False)
class Values(Schema):
timestamp = fields.String(required=True)
value = fields.Float(required =True, validate = Range(min = 0.0, max = 100.0))