Search code examples
pythonmarshmallow

Marshmallow not returning validation errors from a nested List


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.


Solution

  • 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))