marshmallow_dataclass package

Table of contents

marshmallow_dataclass.dataclass([_cls, …])

This decorator does the same as dataclasses.dataclass, but also applies add_schema().

marshmallow_dataclass.class_schema(clazz[, …])

Convert a class to a marshmallow schema

Module

This library allows the conversion of python 3.7’s dataclasses to marshmallow schemas.

It takes a python class, and generates a marshmallow schema for it.

Simple example:

from marshmallow import Schema
from marshmallow_dataclass import dataclass

@dataclass
class Point:
  x:float
  y:float

point = Point(x=0, y=0)
point_json = Point.Schema().dumps(point)

Full example:

from marshmallow import Schema
from dataclasses import field
from marshmallow_dataclass import dataclass
import datetime

@dataclass
class User:
  birth: datetime.date = field(metadata= {
    "required": True # A parameter to pass to marshmallow's field
  })
  website:str = field(metadata = {
    "marshmallow_field": marshmallow.fields.Url() # Custom marshmallow field
  })
  Schema: ClassVar[Type[Schema]] = Schema # For the type checker
marshmallow_dataclass.dataclass(_cls: Type[_U] = None, *, repr: bool = True, eq: bool = True, order: bool = False, unsafe_hash: bool = False, frozen: bool = False, base_schema: Optional[Type[marshmallow.schema.Schema]] = None) → Union[Type[_U], Callable[[Type[_U]], Type[_U]]][source]

This decorator does the same as dataclasses.dataclass, but also applies add_schema(). It adds a .Schema attribute to the class object

Parameters

base_schema – marshmallow schema used as a base class when deriving dataclass schema

>>> @dataclass
... class Artist:
...    name: str
>>> Artist.Schema
<class 'marshmallow.schema.Artist'>
>>> from typing import ClassVar
>>> from marshmallow import Schema
>>> @dataclass(order=True) # preserve field order
... class Point:
...   x:float
...   y:float
...   Schema: ClassVar[Type[Schema]] = Schema # For the type checker
...
>>> Point.Schema().load({'x':0, 'y':0}) # This line can be statically type checked
Point(x=0.0, y=0.0)
marshmallow_dataclass.add_schema(_cls=None, base_schema=None)[source]

This decorator adds a marshmallow schema as the ‘Schema’ attribute in a dataclass. It uses class_schema() internally.

Parameters
  • cls (type) – The dataclass to which a Schema should be added

  • base_schema – marshmallow schema used as a base class when deriving dataclass schema

>>> class BaseSchema(marshmallow.Schema):
...   def on_bind_field(self, field_name, field_obj):
...     field_obj.data_key = (field_obj.data_key or field_name).upper()
>>> @add_schema(base_schema=BaseSchema)
... @dataclasses.dataclass
... class Artist:
...    names: Tuple[str, str]
>>> artist = Artist.Schema().loads('{"NAMES": ["Martin", "Ramirez"]}')
>>> artist
Artist(names=('Martin', 'Ramirez'))
marshmallow_dataclass.class_schema(clazz: type, base_schema: Optional[Type[marshmallow.schema.Schema]] = None) → Type[marshmallow.schema.Schema][source]

Convert a class to a marshmallow schema

Parameters
  • clazz – A python class (may be a dataclass)

  • base_schema – marshmallow schema used as a base class when deriving dataclass schema

Returns

A marshmallow Schema corresponding to the dataclass

Note

All the arguments supported by marshmallow field classes are can be passed in the metadata dictionary of a field.

If you want to use a custom marshmallow field (one that has no equivalent python type), you can pass it as the marshmallow_field key in the metadata dictionary.

>>> import typing
>>> Meters = typing.NewType('Meters', float)
>>> @dataclasses.dataclass()
... class Building:
...   height: Optional[Meters]
...   name: str = dataclasses.field(default="anonymous")
...   class Meta:
...     ordered = True
...
>>> class_schema(Building) # Returns a marshmallow schema class (not an instance)
<class 'marshmallow.schema.Building'>
>>> @dataclasses.dataclass()
... class City:
...   name: str = dataclasses.field(metadata={'required':True})
...   best_building: Building # Reference to another dataclasses. A schema will be created for it too.
...   other_buildings: List[Building] = dataclasses.field(default_factory=lambda: [])
...
>>> citySchema = class_schema(City)()
>>> city = citySchema.load({"name":"Paris", "best_building": {"name": "Eiffel Tower"}})
>>> city
City(name='Paris', best_building=Building(height=None, name='Eiffel Tower'), other_buildings=[])
>>> citySchema.load({"name":"Paris"})
Traceback (most recent call last):
    ...
marshmallow.exceptions.ValidationError: {'best_building': ['Missing data for required field.']}
>>> city_json = citySchema.dump(city)
>>> city_json['best_building'] # We get an OrderedDict because we specified order = True in the Meta class
OrderedDict([('height', None), ('name', 'Eiffel Tower')])
>>> @dataclasses.dataclass()
... class Person:
...   name: str = dataclasses.field(default="Anonymous")
...   friends: List['Person'] = dataclasses.field(default_factory=lambda:[]) # Recursive field
...
>>> person = class_schema(Person)().load({
...     "friends": [{"name": "Roger Boucher"}]
... })
>>> person
Person(name='Anonymous', friends=[Person(name='Roger Boucher', friends=[])])
>>> @dataclasses.dataclass()
... class C:
...   important: int = dataclasses.field(init=True, default=0)
...    # Only fields that are in the __init__ method will be added:
...   unimportant: int = dataclasses.field(init=False, default=0)
...
>>> c = class_schema(C)().load({
...     "important": 9, # This field will be imported
...     "unimportant": 9 # This field will NOT be imported
... }, unknown=marshmallow.EXCLUDE)
>>> c
C(important=9, unimportant=0)
>>> @dataclasses.dataclass
... class Website:
...  url:str = dataclasses.field(metadata = {
...    "marshmallow_field": marshmallow.fields.Url() # Custom marshmallow field
...  })
...
>>> class_schema(Website)().load({"url": "I am not a good URL !"})
Traceback (most recent call last):
    ...
marshmallow.exceptions.ValidationError: {'url': ['Not a valid URL.']}
>>> @dataclasses.dataclass
... class NeverValid:
...     @marshmallow.validates_schema
...     def validate(self, data, **_):
...         raise marshmallow.ValidationError('never valid')
...
>>> class_schema(NeverValid)().load({})
Traceback (most recent call last):
    ...
marshmallow.exceptions.ValidationError: {'_schema': ['never valid']}
>>> # noinspection PyTypeChecker
>>> class_schema(None) # unsupported type
Traceback (most recent call last):
  ...
TypeError: None is not a dataclass and cannot be turned into one.
>>> @dataclasses.dataclass
... class Anything:
...     class Meta:
...         pass
...     @marshmallow.validates('name')
...     def validates(self, *args, **kwargs):
...         pass
...     @marshmallow.validates_schema
...     def validates_schema(self, *args, **kwargs):
...         pass
...     def custom_method(self, *args, **kwargs):
...         pass
...     @property
...     def custom_property(self, *args, **kwargs):
...         return None
>>> AnythingSchema = class_schema(Anything)()
>>> hasattr(AnythingSchema, 'Meta')
True
>>> hasattr(AnythingSchema, 'validates')
True
>>> hasattr(AnythingSchema, 'validates_schema')
True
>>> hasattr(AnythingSchema, 'custom_method')
False
>>> hasattr(AnythingSchema, 'custom_property')
False
marshmallow_dataclass.field_for_schema(typ: type, default=<marshmallow.missing>, metadata: Mapping[str, Any] = None, base_schema: Optional[Type[marshmallow.schema.Schema]] = None) → marshmallow.fields.Field[source]

Get a marshmallow Field corresponding to the given python type. The metadata of the dataclass field is used as arguments to the marshmallow Field.

Parameters

base_schema – marshmallow schema used as a base class when deriving dataclass schema

>>> int_field = field_for_schema(int, default=9, metadata=dict(required=True))
>>> int_field.__class__
<class 'marshmallow.fields.Integer'>
>>> int_field.default
9
>>> int_field.required
True
>>> field_for_schema(Dict[str,str]).__class__
<class 'marshmallow.fields.Dict'>
>>> field_for_schema(str, metadata={"marshmallow_field": marshmallow.fields.Url()}).__class__
<class 'marshmallow.fields.Url'>
>>> field_for_schema(Optional[str]).__class__
<class 'marshmallow.fields.String'>
>>> from enum import Enum
>>> import marshmallow_enum
>>> field_for_schema(Enum("X", "a b c")).__class__
<class 'marshmallow_enum.EnumField'>
>>> import typing
>>> field_for_schema(typing.Union[int,str]).__class__
<class 'marshmallow_union.Union'>
>>> field_for_schema(typing.NewType('UserId', int)).__class__
<class 'marshmallow.fields.Integer'>
>>> field_for_schema(typing.NewType('UserId', int), default=0).default
0
>>> class Color(Enum):
...   red = 1
>>> field_for_schema(Color).__class__
<class 'marshmallow_enum.EnumField'>
>>> field_for_schema(Any).__class__
<class 'marshmallow.fields.Raw'>
marshmallow_dataclass.NewType(name: str, typ: Type[_U], field: Optional[Type[marshmallow.fields.Field]] = None, **kwargs) → Callable[[_U], _U][source]

NewType creates simple unique types to which you can attach custom marshmallow attributes. All the keyword arguments passed to this function will be transmitted to the marshmallow field constructor.

>>> import marshmallow.validate
>>> IPv4 = NewType('IPv4', str, validate=marshmallow.validate.Regexp(r'^([0-9]{1,3}\.){3}[0-9]{1,3}$'))
>>> @dataclass
... class MyIps:
...   ips: List[IPv4]
>>> MyIps.Schema().load({"ips": ["0.0.0.0", "grumble grumble"]})
Traceback (most recent call last):
...
marshmallow.exceptions.ValidationError: {'ips': {1: ['String does not match expected pattern.']}}
>>> MyIps.Schema().load({"ips": ["127.0.0.1"]})
MyIps(ips=['127.0.0.1'])
>>> Email = NewType('Email', str, field=marshmallow.fields.Email)
>>> @dataclass
... class ContactInfo:
...   mail: Email = dataclasses.field(default="anonymous@example.org")
>>> ContactInfo.Schema().load({})
ContactInfo(mail='anonymous@example.org')
>>> ContactInfo.Schema().load({"mail": "grumble grumble"})
Traceback (most recent call last):
...
marshmallow.exceptions.ValidationError: {'mail': ['Not a valid email address.']}