marshmallow_dataclass package¶
Table of contents¶
This decorator does the same as dataclasses.dataclass, but also applies |
|
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.NewType(name: str, typ: Type[_U], field: Type[Field] | None = 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.']}
- marshmallow_dataclass.add_schema(_cls: Type[_U]) Type[_U] [source]¶
- marshmallow_dataclass.add_schema(base_schema: Type[Schema] | None = None) Callable[[Type[_U]], Type[_U]]
- marshmallow_dataclass.add_schema(_cls: Type[_U], base_schema: Type[Schema] | None = None, cls_frame: frame | None = None, stacklevel: int = 1) Type[_U]
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
cls_frame – frame of cls definition
>>> 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: Type[Schema] | None = None, *, globalns: Dict[str, Any] | None = None, localns: Dict[str, Any] | None = None) Type[Schema] [source]¶
- marshmallow_dataclass.class_schema(clazz: type, base_schema: Type[Schema] | None = None, clazz_frame: frame | None = None, *, globalns: Dict[str, Any] | None = None) Type[Schema]
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
clazz_frame – frame of cls definition
- Returns:
A marshmallow Schema corresponding to the dataclass
Note
All the arguments supported by marshmallow field classes 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 dataclass. 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=[])])
Marking dataclass fields as non-initialized (
init=False
), by default, will result in those fields from being exluded in the schema. To override this behaviour, set theMeta
optioninclude_non_init=True
.>>> @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 C: ... class Meta: ... include_non_init = True ... important: int = dataclasses.field(init=True, default=0) ... 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 be imported ... }, unknown=marshmallow.EXCLUDE) >>> c C(important=9, unimportant=9)
>>> @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']}
>>> @dataclasses.dataclass ... class Anything: ... name: str ... @marshmallow.validates('name') ... def validates(self, value): ... if len(value) > 5: raise marshmallow.ValidationError("Name too long") >>> class_schema(Anything)().load({"name": "aaaaaargh"}) Traceback (most recent call last): ... marshmallow.exceptions.ValidationError: {'name': ['Name too long']}
You can use the
metadata
argument to override default field behaviour, e.g. the fact thatOptional
fields allowNone
values:>>> @dataclasses.dataclass ... class Custom: ... name: Optional[str] = dataclasses.field(metadata={"allow_none": False}) >>> class_schema(Custom)().load({"name": None}) Traceback (most recent call last): ... marshmallow.exceptions.ValidationError: {'name': ['Field may not be null.']} >>> class_schema(Custom)().load({}) Custom(name=None)
- marshmallow_dataclass.dataclass(_cls: Type[_U], *, repr: bool = True, eq: bool = True, order: bool = False, unsafe_hash: bool = False, frozen: bool = False, base_schema: Type[Schema] | None = None, cls_frame: frame | None = None) Type[_U] [source]¶
- marshmallow_dataclass.dataclass(*, repr: bool = True, eq: bool = True, order: bool = False, unsafe_hash: bool = False, frozen: bool = False, base_schema: Type[Schema] | None = None, cls_frame: frame | None = None) Callable[[Type[_U]], Type[_U]]
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
cls_frame – frame of cls definition, used to obtain locals with other classes definitions. If None is passed the caller frame will be treated as cls_frame
>>> @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.field_for_schema(typ: type, default: ~typing.Any = <marshmallow.missing>, metadata: ~typing.Mapping[str, ~typing.Any] | None = None, base_schema: ~typing.Type[~marshmallow.schema.Schema] | None = None, typ_frame: frame | None = None) 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:
typ – The type for which a field should be generated
default – value to use for (de)serialization when the field is missing
metadata – Additional parameters to pass to the marshmallow field constructor
base_schema – marshmallow schema used as a base class when deriving dataclass schema
typ_frame – frame of type definition
>>> int_field = field_for_schema(int, default=9, metadata=dict(required=True)) >>> int_field.__class__ <class 'marshmallow.fields.Integer'>
>>> int_field.dump_default 9
>>> field_for_schema(str, metadata={"marshmallow_field": marshmallow.fields.Url()}).__class__ <class 'marshmallow.fields.Url'>