Solution
Based on @bwl1289 answer.
In addition, this custom field is inspired by from typing import Union
.
# encoding: utf-8
"""
Marshmallow fields
------------------
Extension on the already available marshmallow fields
"""
from marshmallow import ValidationError, fields
class UnionField(fields.Field):
"""Field that deserializes multi-type input data to app-level objects."""
def __init__(self, types: list = [], *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
if types:
self.types = types
else:
raise AttributeError('No types provided on union field')
def _deserialize(self, value, attr, data, **kwargs):
if bool([isinstance(value, i) for i in self.types if isinstance(value, i)]):
return value
else:
raise ValidationError(
f'Field shoud be any of the following types: [{", ".join([str(i) for i in self.types])}]'
)
__init__(self, types)
- New parameter "types". Which accepts a list of default types within Python alongside the default parameters of a marshmallow field.
- super copies default class in current class.
- If this "types" parameter is empty we raises by default an
AttributeError
.
_deserialize()
- Checks if current value is
oneof
the self.types
provided in the __init__.
- Raises ValidationError with a formatted error message based on
self.types
.
Example
# encoding: utf-8
"""
Example
-------
Example for utilization
"""
from marshmallow import Schema
class AllTypes(Schema):
"""
Example schema
"""
some_field = UnionField(
types=[str, int, float, dict, list, bool, set, tuple],
metadata={
"description": "Multiple types.",
},
)
UnitTest
# encoding: utf-8
"""
Test custom marshmallow fields
"""
from marshmallow import Schema, ValidationError
import pytest
def test_union_field():
class MultiType(Schema):
test = UnionField(
types=[str, int],
metadata={
"description": "String and Integer.",
},
)
class AllTypes(Schema):
test = UnionField(
types=[str, int, float, dict, list, bool, set, tuple],
metadata={
"description": "Multiple types",
},
)
with pytest.raises(AttributeError):
class NoTypes(Schema):
test = UnionField(
types=[],
metadata={
"description": "No Type.",
},
)
m = MultiType()
m.load({'test': 'test'})
m.load({'test': 123})
with pytest.raises(ValidationError):
m.load({'test': 123.123})
m.load({'test': {'test': 'test'}})
m.load({'test': ['test', 'test']})
m.load({'test': False})
m.load({'test': set([1, 1, 2, 3, 4])})
m.load({'test': (1, 1, 2, 3, 4,)})
a = AllTypes()
a.load({'test': 'test'})
a.load({'test': 123})
a.load({'test': 123.123})
a.load({'test': {'test': 'test'}})
a.load({'test': ['test', 'test']})
a.load({'test': False})
a.load({'test': set([1, 1, 2, 3, 4])})
a.load({'test': (1, 1, 2, 3, 4,)})
assert 1 == 1