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 169 170 171 172 173
|
Custom fields
=============
There are three ways to create a custom-formatted field for a `Schema <marshmallow.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
------------------------------------------------
.. warning::
The ``context`` attribute is deprecated and will be removed in marshmallow 4.
Use `contextvars.ContextVar` for passing context to fields, pre-/post-processing methods, and validators instead.
marshmallow 4 will also provide an `experimental helper API <https://marshmallow.readthedocs.io/en/latest/marshmallow.experimental.context.html>`_
for using context.
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 <marshmallow.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/index` page.
- For example applications using marshmallow, check out the :doc:`examples/index` page.
|