25

Is it possible to validate list using marshmallow?

class SimpleListInput(Schema):
    items = fields.List(fields.String(), required=True)

# expected invalid type error
data, errors = SimpleListInput().load({'some': 'value'})

# should be ok 
data, errors = SimpleListInput().load(['some', 'value'])

Or it is expected to validate only objects?

avasin
  • 9,186
  • 18
  • 80
  • 127
  • The problem might be that iterating over the dictionary *does* give you strings; what if you pass a dictionary whose keys are e.g. integers? – jonrsharpe May 15 '16 at 10:55
  • Are you checking the value of `errors`? When I try your example, it seems to get set to an appropriate value. – larsks May 15 '16 at 10:59

4 Answers4

38

To validate top-level lists, you need to instantiate your list item schema with many=True argument.

Example:

class UserSchema(marshmallow.Schema):
    name = marshmallow.fields.String()

data, errors = UserSchema(many=True).load([
    {'name': 'John Doe'},
    {'name': 'Jane Doe'}
])

But it still needs to be an object schema, Marshmallow does not support using top-level non-object lists. In case you need to validate top-level list of non-object types, a workaround would be to define a schema with one List field of your types and just wrap payload as if it was an object:

class SimpleListInput(marshmallow.Schema):
    items = marshmallow.fields.List(marshmallow.fields.String(), required=True)

payload = ['foo', 'bar']
data, errors = SimpleListInput().load({'items': payload})
Maxim Kulkin
  • 2,698
  • 22
  • 11
  • "Marshmallow does not support using top-level non-object lists." -- That's exactly the information I was looking for because that's what I'm trying to do. How do you learn this? Is it documented? – odigity Aug 13 '23 at 21:10
14

SimpleListInput is a class with a property "items". The property "items" is who accepts a list of strings.

>>> data, errors = SimpleListInput().load({'items':['some', 'value']})
>>> print data, errors
{'items': [u'some', u'value']} 
{}
>>> data, errors = SimpleListInput().load({'items':[]})
>>> print data, errors
{'items': []} 
{}
>>> data, errors = SimpleListInput().load({})
>>> print data, errors
{} 
{'items': [u'Missing data for required field.']}

If you want a custom validate, for example, not accept an empty list in "items":

from marshmallow import fields, Schema, validates, ValidationError

class SimpleListInput(Schema):
    items = fields.List(fields.String(), required=True)

    @validates('items')
    def validate_length(self, value):
        if len(value) < 1:
            raise ValidationError('Quantity must be greater than 0.') 

Then...

>>> data, errors = SimpleListInput().load({'items':[]})
>>> print data, errors
{'items': []} 
{'items': ['Quantity must be greater than 0.']}

Take a look at Validation

UPDATE:

As @Turn commented below. You can do this:

from marshmallow import fields, Schema, validate

class SimpleListInput(Schema):        
    items = fields.List(fields.String(), required=True, validate=validate.Length(min=1))
fishtek
  • 33
  • 5
Jair Perrut
  • 1,360
  • 13
  • 24
5

Please take a look at a little library written by me which tries to solve exactly this problem: https://github.com/and-semakin/marshmallow-toplevel.

Installation:

pip install marshmallow-toplevel

Usage (on the example from Maxim Kulkin):

import marshmallow
from marshmallow_toplevel import TopLevelSchema

class SimpleListInput(TopLevelSchema):
    _toplevel = marshmallow.fields.List(
        marshmallow.fields.String(),
        required=True,
        validate=marshmallow.validate.Length(1, 10)
    )

# raises ValidationError, because:
# Length must be between 1 and 10.
SimpleListInput().load([])

# raises ValidationError, because:
# Length must be between 1 and 10.
SimpleListInput().load(["qwe" for _ in range(11)])

# successfully loads data
payload = ["foo", "bar"]
data = SimpleListInput().load(payload)
assert data == ["foo", "bar"]

Of course it can be used with more complex schemas than just string in example.

Andrey Semakin
  • 2,032
  • 1
  • 23
  • 50
0

It is possible to use a Field type directly [documentation]:

simple_list_input = fields.List(fields.String(), required=True)

# ValidationError: Not a valid list.
simple_list_input.deserialize({'some': 'value'})

# ValidationError: {0: ['Not a valid string.']}
simple_list_input.deserialize([1, 'value'])

# Returns: ['some', 'value'] with no errors
simple_list_input.deserialize(['some', 'value'])

In comparison with Schema:

  • deserialize == load
  • serialize == dump
Eric M.
  • 2,507
  • 2
  • 10
  • 15