Marshmallow: Error when running Custom Fields documented example

Created on 13 Mar 2020  路  4Comments  路  Source: marshmallow-code/marshmallow

Hello,

I was trying to run the Custom Field example but I get an error related to the keys being duplicate.

This is the example:

from marshmallow import fields, Schema, pprint


class TitleCased(fields.Field):
    """Field that serializes to a title case string and deserializes
    to a lower case string.
    """

    def _serialize(self, value, attr, obj, **kwargs):
        if value is None:
            return ""
        return value.title()

    def _deserialize(self, value, attr, data, **kwargs):
        return value.lower()


class UserSchema(Schema):
    name = fields.String()
    email = fields.String()
    created_at = fields.DateTime()
    titlename = TitleCased(attribute="name")

This is the code snipet I use to run the example since the example seems to miss to tell how to actually test the example:

user_schema = UserSchema()
user = user_schema.load({"name": "John Doe", "email": "[email protected]", "created_at": datetime.utcnow().isoformat()})
pprint(user)

This is the result:

(venv) PS D:\Documents\Projects\research\marsh> python.exe .\poc.py                                                     Traceback (most recent call last):
  File ".\poc.py", line 27, in <module>
    user_schema = UserSchema()
  File "D:\Documents\Projects\research\marsh\venv\lib\site-packages\marshmallow\schema.py", line 397, in __init__
    self._init_fields()
  File "D:\Documents\Projects\research\marsh\venv\lib\site-packages\marshmallow\schema.py", line 1008, in _init_fields
    raise ValueError(
ValueError: The attribute argument for one or more fields collides with another field's name or attribute argument. Check the following field names and attribute arguments: ['name']

But if I change the schema definition to this:

class UserSchema(Schema):
    # name = fields.String()
    email = fields.String()
    created_at = fields.DateTime()
    name = TitleCased(attribute="name")

Then I get the following output:

{'created_at': datetime.datetime(2020, 3, 13, 20, 46, 42, 817528),
 'email': '[email protected]',
 'name': 'john doe'}

In conclusion I'm not sure if it's me doing something wrong or the documentation is not complete for this particular feature.

I'll gladly accept any help regarding this.
Cheers.

docs feedback welcome

Most helpful comment

I was able to reproduce that behavior in the latest release. That example would need to set dump_only=True to avoid the collision and not try to read a computed value.

It would be even better if the example avoided reusing a field name to keep things as simple as possible. Maybe that example was chosen to keep the examples consistent with the method/function examples after it. That is not great use case for using a custom field though. Ideally it should be a simple transformation with validation.

from marshmallow import fields, ValidationError


class PinCode(fields.Field):
    """Field that serializes to a string of numbers and deserializes
    to a list of numbers.
    """

    def _serialize(self, value, attr, obj, **kwargs):
        if value is None:
            return ""
        return ''.join(str(d) for d in value)

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            return [int(c) for c in value]
        except ValueError:
            raise ValidationError('Pin codes must contain only digits')


class UserSchema(Schema):
    name = fields.String()
    email = fields.String()
    created_at = fields.DateTime()
    pin_code = PinCode()

All 4 comments

I was able to reproduce that behavior in the latest release. That example would need to set dump_only=True to avoid the collision and not try to read a computed value.

It would be even better if the example avoided reusing a field name to keep things as simple as possible. Maybe that example was chosen to keep the examples consistent with the method/function examples after it. That is not great use case for using a custom field though. Ideally it should be a simple transformation with validation.

from marshmallow import fields, ValidationError


class PinCode(fields.Field):
    """Field that serializes to a string of numbers and deserializes
    to a list of numbers.
    """

    def _serialize(self, value, attr, obj, **kwargs):
        if value is None:
            return ""
        return ''.join(str(d) for d in value)

    def _deserialize(self, value, attr, data, **kwargs):
        try:
            return [int(c) for c in value]
        except ValueError:
            raise ValidationError('Pin codes must contain only digits')


class UserSchema(Schema):
    name = fields.String()
    email = fields.String()
    created_at = fields.DateTime()
    pin_code = PinCode()

Thanks for taking the time to check this and clarifying the issue.

I was trying to do something like your example in my actual implementation, that's why I was reading the docs in the first place and got to that misleading example.

I've achieved something similar to your example while experimenting in the weekend which, at least for me, it better demonstrates the potential of custom fields.

I like the above example better than what's currently there. @deckar01 Would you like to update the docs?

Nice example @deckar01, updated docs in #1585 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jayennis22 picture jayennis22  路  4Comments

Ovyerus picture Ovyerus  路  3Comments

symonk picture symonk  路  3Comments

k0nsta picture k0nsta  路  4Comments

tadams42 picture tadams42  路  3Comments