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.
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)
Most helpful comment
@samjetski is correct that @sloria 's code doesn't work for the issue.
This helped me: