File: custom_fields.rst

package info (click to toggle)
python-marshmallow 3.10.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 1,076 kB
  • sloc: python: 10,539; makefile: 14
file content (168 lines) | stat: -rw-r--r-- 5,836 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

Custom Fields
=============

There are three ways to create a custom-formatted field for a `Schema`:

- Create a custom :class:`Field <marshmallow.fields.Field>` class
- Use a :class:`Method <marshmallow.fields.Method>` field
- Use a :class:`Function <marshmallow.fields.Function>` field

The method you choose will depend on the manner in which you intend to reuse the field.

Creating A Field Class
----------------------

To create a custom field class, create a subclass of :class:`marshmallow.fields.Field` and implement its :meth:`_serialize <marshmallow.fields.Field._serialize>` and/or :meth:`_deserialize <marshmallow.fields.Field._deserialize>` methods.

.. code-block:: python

    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 as error:
                raise ValidationError("Pin codes must contain only digits.") from error


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

Method Fields
-------------

A :class:`Method <marshmallow.fields.Method>` field will serialize to the value returned by a method of the Schema. The method must take an ``obj`` parameter which is the object to be serialized.

.. code-block:: python

    class UserSchema(Schema):
        name = fields.String()
        email = fields.String()
        created_at = fields.DateTime()
        since_created = fields.Method("get_days_since_created")

        def get_days_since_created(self, obj):
            return dt.datetime.now().day - obj.created_at.day

Function Fields
---------------

A :class:`Function <marshmallow.fields.Function>` field will serialize the value of a function that is passed directly to it. Like a :class:`Method <marshmallow.fields.Method>` field, the function must take a single argument ``obj``.


.. code-block:: python

    class UserSchema(Schema):
        name = fields.String()
        email = fields.String()
        created_at = fields.DateTime()
        uppername = fields.Function(lambda obj: obj.name.upper())

`Method` and `Function` field deserialization
---------------------------------------------

Both :class:`Function <marshmallow.fields.Function>` and :class:`Method <marshmallow.fields.Method>` receive an optional ``deserialize`` argument which defines how the field should be deserialized. The method or function passed to ``deserialize`` receives the input value for the field.

.. code-block:: python

    class UserSchema(Schema):
        # `Method` takes a method name (str), Function takes a callable
        balance = fields.Method("get_balance", deserialize="load_balance")

        def get_balance(self, obj):
            return obj.income - obj.debt

        def load_balance(self, value):
            return float(value)


    schema = UserSchema()
    result = schema.load({"balance": "100.00"})
    result["balance"]  # => 100.0

.. _adding-context:

Adding Context to `Method` and `Function` Fields
------------------------------------------------

A :class:`Function <marshmallow.fields.Function>` or :class:`Method <marshmallow.fields.Method>` field may need information about its environment to know how to serialize a value.

In these cases, you can set the ``context`` attribute (a dictionary) of a `Schema`. :class:`Function <marshmallow.fields.Function>` and :class:`Method <marshmallow.fields.Method>` fields will have access to this dictionary.

As an example, you might want your ``UserSchema`` to output whether or not a ``User`` is the author of a ``Blog`` or whether a certain word appears in a ``Blog's`` title.

.. code-block:: python

    class UserSchema(Schema):
        name = fields.String()
        # Function fields optionally receive context argument
        is_author = fields.Function(lambda user, context: user == context["blog"].author)
        likes_bikes = fields.Method("writes_about_bikes")

        def writes_about_bikes(self, user):
            return "bicycle" in self.context["blog"].title.lower()


    schema = UserSchema()

    user = User("Freddie Mercury", "fred@queen.com")
    blog = Blog("Bicycle Blog", author=user)

    schema.context = {"blog": blog}
    result = schema.dump(user)
    result["is_author"]  # => True
    result["likes_bikes"]  # => True


Customizing Error Messages
--------------------------

Validation error messages for fields can be configured at the class or instance level.

At the class level, default error messages are defined as a mapping from error codes to error messages.

.. code-block:: python

    from marshmallow import fields


    class MyDate(fields.Date):
        default_error_messages = {"invalid": "Please provide a valid date."}

.. note::
    A `Field's` ``default_error_messages`` dictionary gets merged with its parent classes' ``default_error_messages`` dictionaries.

Error messages can also be passed to a `Field's` constructor.

.. code-block:: python

    from marshmallow import Schema, fields


    class UserSchema(Schema):

        name = fields.Str(
            required=True, error_messages={"required": "Please provide a name."}
        )


Next Steps
----------

- Need to add schema-level validation, post-processing, or error handling behavior? See the :doc:`Extending Schemas <extending>` page.
- For example applications using marshmallow, check out the :doc:`Examples <examples>` page.