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?
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
valueis a sequence and each element in the sequence is also in the sequence passed aschoices
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).
Most helpful comment
After giving this some thought, I think
ContainsOnlyshould accept empty values as valid inputs. Validating against empty vs. non-empty is outside the responsibility ofContainsOnly. If you need to validate that input is non-empty, useLength(min=1).