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]¶
DEPRECATED: Use typing.Annotated instead. 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: FrameType | 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: FrameType | 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: FrameType | 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: FrameType | 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: ~types.FrameType | 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'>