Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set a lower bound for Int field in marshmallow

Is it possible to set a lower / upper bound for a filds.Int in marshmallow?

What I want to do is

from marshmallow import Schema, fields

class User(Schema):
    age = fields.Int()

u = User()
data = u.load({'age': -1})

>>> data
{'age': 0}

In the case, I want to set 0 as a lower-bound value for age.

like image 223
andrewshih Avatar asked Sep 18 '25 22:09

andrewshih


1 Answers

It does not seem like you can set a bound that will alter your input value upon deserialization using Marshmallow's api; and it might be for good reason: it would be very confusing to see something like data = u.load({'age': -1}) and getting {'age': 0} back.

What you can do is pass a function to fields.Int's validate parameter that would allow you to catch values you don't want. Example usage:

class User(Schema):
    age = fields.Int(validate=lambda x: x > 0)  # don't let anything <0 in


u = User()
data = u.load({'age': -1}

Output:

marshmallow.exceptions.ValidationError: {'age': ['Invalid value.']}

With this information in mind, you can handle this exception as you please, e.g.:

try:
    data = u.load({'age': -1})
except marshmallow.exceptions.ValidationError:
    data = u.load({'age': 0})

If you feel that you must create some bounds for your fields.Int usage, then you can extend fields.Int in the following manner:

import typing 

from marshmallow import fields, Schema

_T = typing.TypeVar("_T")


class BoundedInt(fields.Int):
    
    def __init__(self, *a, bounds, **kw):
        self._bounds: typing.Tuple[int] = bounds  # bounds=(0, 10)
        super().__init__(*a, **kw)
    
    def _validated(self, value) -> typing.Optional[_T]:
        if value < self._bounds[0]:
            value = self._bounds[0]
        elif value > self._bounds[1]:
            value = self._bounds[1]
        return super()._validated(value)
    

class User(Schema):
    age = BoundedInt(bounds=(0, 10))

Usage:

>>> u = User()
>>> data = u.load({'age': -1})
>>> data
{'age': 0}
like image 83
Algebra8 Avatar answered Sep 21 '25 10:09

Algebra8