Marshmallow: How to create a schema that has no

Created on 23 Aug 2016  路  8Comments  路  Source: marshmallow-code/marshmallow

Given the following schema:

class CalendarSchema(Schema):
    days_of_month = fields.List(fields.Integer(), default=[], validate=validate.ContainsOnly(range(1, 31 + 1)))

The above schema works fine as long as the array passed contains the value between [1, 31]. However, if the array was an empty array ([]), the validation fails. How can I tweak fields.List such that it accepts an empty array?

needs review

Most helpful comment

After giving this some thought, I think ContainsOnly should accept empty values as valid inputs. Validating against empty vs. non-empty is outside the responsibility of ContainsOnly. If you need to validate that input is non-empty, use Length(min=1).

All 8 comments

Looks like it's a bug in ContainsOnly validator which I guess treats None values and empty arrays the same (see these lines ). Also, I just found out that this validator is a bit tricky, because values can not be mentioned twice (which is not obvious from docstring).

Solution for you would be either to write your own custom validator or to wait till bug in ContainsOnly is fixed.

class CalendarSchema(Schema):
    days_of_month = fields.List(fields.Integer(), default=[])

    @validates('days_of_month')
    def validate_days_of_month(self, data):
        for item in data:
            if item not in range(1, 31 + 1):
                raise ValidationError('Contains invalid value %s' % item)

It seems that not allow empty values is put-up behavior. See test:
https://github.com/marshmallow-code/marshmallow/blob/dev/tests/test_validate.py#L607-L608

Would be nice to have ContainsOnly accepting a param like allow_empty to make empty lists allowed

I'm going to hold off on moving forward with this. You can use a custom validator if you want to accept empty collections as valid input.

from marshmallow import validate, Schema, fields
from marshmallow.utils import is_iterable_but_not_string


class LenientContainsOnly(validate.ContainsOnly):

    def __call__(self, value):
        if is_iterable_but_not_string(value) and not len(value):
            return True
        return super().__call__(value)

class MySchema(Schema):
    foo = fields.List(fields.Int(), validate=[LenientContainsOnly([1, 2])])

    class Meta:
        strict = True

sch = MySchema()
assert sch.load({'foo': []}).data == {'foo': []}

As pointed out by @maximkulkin

validate.ContainsOnly([1, 2, 3])([1, 2, 3, 3])

raises ValidationError.

It seems intended (explicit del choices[index] in the code) but is not explicit in the docs, and in fact, I don't get the rationale.

The docs says

succeeds if value is a sequence and each element in the sequence is also in the sequence passed as choices

Good point, @lafrech and @maximkulkin . That specific behavior seems incorrect; I'll open a new issue.

After giving this some thought, I think ContainsOnly should accept empty values as valid inputs. Validating against empty vs. non-empty is outside the responsibility of ContainsOnly. If you need to validate that input is non-empty, use Length(min=1).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jayennis22 picture jayennis22  路  4Comments

lupodellasleppa picture lupodellasleppa  路  3Comments

ambye85 picture ambye85  路  4Comments

Ovyerus picture Ovyerus  路  3Comments

imhoffd picture imhoffd  路  3Comments