Marshmallow: Support Werkzeug MultiDict in fields.List

Created on 24 Jan 2015  路  4Comments  路  Source: marshmallow-code/marshmallow

I'm talking about werkzeug.datastructures.ImmutableMultiDict. This is the type flask.request.values will give you. (actually it's a CombinedMultiDict but they have the same interface).

You can get a single value out of it with [] or get(), or a list of values with getlist(). It would be neat if marshmallow's fields.List() knew to check for the getlist() method, in case it is given a multidict that can return a list in this way.

Most helpful comment

@samjetski is correct that @sloria 's code doesn't work for the issue.

This helped me:

class List(marshmallow.fields.List):
    def _deserialize(self, value, attr, data):
        if isinstance(data, dict) and hasattr(data, 'getlist'):
            value = data.getlist(attr)
        return super()._deserialize(value, attr, data)

All 4 comments

Thanks for the suggestion. I'm going to hold off on adding built-in support for MultiDicts because it is a 3rd-party interface (albeit a very common one among the web frameworks). You can add support relatively easily by overriding a Schema's accessor:

from marshmallow import Schema, fields, pprint
from marshmallow.utils import get_value

from werkzeug.datastructures import ImmutableMultiDict

def multidict_aware(schema, key, obj, default=None):
    # Handle MultiDicts
    if isinstance(obj, dict) and hasattr(obj, 'getlist'):
        return obj.getlist(key)
    return get_value(key, obj, default)


class MySchema(Schema):
    __accessor__ = multidict_aware
    vals = fields.List(fields.Str)

d = ImmutableMultiDict([('vals', 'a'), ('vals', 'b')])
s = MySchema()
s.dump(d).data  # {'vals': ['a', 'b']}

Closing this for now, since we won't be adding built-in support for multidicts.

For anyone else struggling with this, I was trying to do a similar thing but using schema.load(request.args) and preserve the MultiDict behaviour for attributes issued multiple times.

The above example only works for serialization (schema.dump()), not deserialization (schema.load()).

The best solution I found was to use the webargs module which wraps marshmallow, and will automatically attempt to use obj.getlist() when loading a fields.List() field. Simply use a fields.List() in your schema and it does the rest.

@samjetski is correct that @sloria 's code doesn't work for the issue.

This helped me:

class List(marshmallow.fields.List):
    def _deserialize(self, value, attr, data):
        if isinstance(data, dict) and hasattr(data, 'getlist'):
            value = data.getlist(attr)
        return super()._deserialize(value, attr, data)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

lassandroan picture lassandroan  路  3Comments

DenisKuplyakov picture DenisKuplyakov  路  4Comments

ambye85 picture ambye85  路  4Comments

imhoffd picture imhoffd  路  3Comments

manoadamro picture manoadamro  路  3Comments