marshmallow_dataclass.class_schema¶
- 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)