How can i specify data_key in Marshmallow as a list of possible key names?

Viewed 947

I was wondering is there any way to specify data_keyin Marshmallow as a list or maybe any other way to tell that some specific field like username can be with the different key names like name, user_name, UserName, userName

Here is example of my current schema

class PersonSchema(Schema):
    name = fields.String(data_key="user_name")
    age = fields.Integer()

    @post_load
    def create_person(self, data, **kwargs):
        return Person(**data)
1 Answers

Currently (as of version 3.13.0), this is how data_key is handled (source):

...

    key = field_obj.data_key if field_obj.data_key is not None else attr_name
    ret[key] = value
return ret

...

So if you try to specify a e.g., tuple as data_key (with a list will not work since it's not hashable), the whole tuple will constitute the key of the dictionary ret (see above). Therefore, it will not work with single strings.

A workaround could be to use the pre_load decorator, e.g.:

from marshmallow import Schema, fields, pre_load, ValidationError


class PersonSchema(Schema):
    name = fields.String()
    age = fields.Integer()

    @pre_load
    def process_data_key(self, data, **kwargs):
        names = [name for name in data.keys()
                 if name in ("name", "user_name", "UserName", "userName")] 
        if len(names) > 1:
            raise ValidationError('Multiple values for field "name".')
        if names:  # check if any name was included at all
            data["name"] = data.pop(names[0])
        return data


s = PersonSchema()

data = {"name": "Mike", "age": "32"}
result = s.load(data)
print(result)  # {'name': 'Mike', 'age': 32}

data = {"user_name": "Mike", "age": "32"}
result = s.load(data)
print(result)  # {'name': 'Mike', 'age': 32}

data = {"UserName": "Mike", "age": "32"}
result = s.load(data)
print(result)  # {'name': 'Mike', 'age': 32}

data = {"name": "Mike", "UserName": "Mike", "age": "32"}
try:
    result = s.load(data)
except ValidationError as err:
    print(err.messages)  # {'_schema': ['Multiple values for field "name".']}

data = {"age": "32"}
result = s.load(data)
print(result)  # {'age': 32}

The idea above is to "map" all the different variations of name (i.e., user_name, UserName, etc.) to name.

Related