marshmallow Documentation

repository·dev·Indexed 27 days ago

https://github.com/marshmallow-code/marshmallow

A lightweight, ORM/ODM/framework-agnostic library for converting complex datatypes to and from native Python datatypes, primarily used for data validation, deserialization, and serialization. Version 4.3.0.

Tokens
24.8K
Snippets
79
Records
121
Agent score
90%

What's inside marshmallow

  1. Advanced schema nesting capabilities

    dev

    Marshmallow supports complex data structures through nested schemas. Key features include:

    • Subset selection: Specifying which fields to include within a nested schema.
    • Two-way nesting: Allowing two different schemas to reference each other.
    • Self-nesting: Allowing a schema to contain an instance of itself.
  2. Use multiple field names with @validates in marshmallow 4.0

    dev

    The @validates <marshmallow.validates> decorator in marshmallow 4.0 now accepts multiple field names as arguments. When multiple fields are provided, the decorated method receives data_key as a keyword argument to identify which field is being validated.

    from marshmallow import fields, Schema, validates
    
    
    class UserSchema(Schema):
        name = fields.Str(required=True)
        nickname = fields.Str(required=True)
    
        @validates("name", "nickname")
        def validate_names(self, value: str, data_key: str) -> None:
            if len(value) < 3:
                raise ValidationError(f'"{data_key}" too short')
  3. Serialize and deserialize data with marshmallow

    dev

    marshmallow is an ORM/ODM/framework-agnostic library used to convert complex datatypes (like objects) to and from native Python datatypes.

    Key capabilities include:

    • Validate input data.
    • Deserialize input data to app-level objects.
    • Serialize app-level objects to primitive Python types (e.g., for JSON rendering in an HTTP API).

    You define schemas by subclassing marshmallow.Schema and specifying fields using marshmallow.fields.

    from datetime import date
    from pprint import pprint
    
    from marshmallow import Schema, fields
    
    
    class ArtistSchema(Schema):
        name = fields.Str()
    
    
    class AlbumSchema(Schema):
        title = fields.Str()
        release_date = fields.Date()
        artist = fields.Nested(ArtistSchema())
    
    
    bowie = dict(name="David Bowie")
    album = dict(artist=bowie, title="Hunky Dory", release_date=date(1971, 12, 17))
    
    schema = AlbumSchema()
    result = schema.dump(album)
    pprint(result, indent=2)
    # { 'artist': {'name': 'David Bowie'},
    #   'release_date': '1971-12-17',
    #   'title': 'Hunky Dory'}
  4. Upgrade to Marshmallow 3.x: Strict Schemas and Error Handling

    dev

    In Marshmallow 3.x, schemas are always strict. The strict parameter has been removed from the Schema constructor and class Meta.

    Schema().load() and Schema().dump() no longer return a (data, errors) tuple; they only return data. If validation fails, a ValidationError is raised. You can access validation errors via ValidationError.messages and the valid data via ValidationError.valid_data.

    from marshmallow import ValidationError
    
    # 3.x pattern
    schema = UserSchema()
    try:
        data = schema.load({"name": "Monty", "email": "monty@python.org"})
    except ValidationError as err:
        errors = err.messages
        valid_data = err.valid_data
  5. Nest a schema within itself (Self-nesting)

    dev

    For objects with relationships to the same type (e.g., a user having friends), nest the schema within itself by passing a callable that returns an instance of the same schema. Use exclude to prevent infinite recursion.

    class UserSchema(Schema):
        name = fields.String()
        email = fields.Email()
        # Use lambda and exclude to prevent infinite recursion
        employer = fields.Nested(lambda: UserSchema(exclude=("employer", "friends")))
        friends = fields.List(
            fields.Nested(lambda: UserSchema(exclude=("employer", "friends")))
        )
  6. Override attribute access in a Schema

    dev

    By default, marshmallow uses marshmallow.utils.get_value to retrieve attributes from objects during serialization. If you need to change this behavior—for example, to ensure that attribute access always uses a specific method like dict.get when serializing dictionaries—you can override the get_attribute method on your Schema class.

    When overriding get_attribute(self, obj, key, default), you are responsible for implementing the logic to extract the value for the given key from the object obj, returning the default value if the key is not found.

    class UserDictSchema(Schema):
        name = fields.Str()
        email = fields.Email()
    
        # If we know we're only serializing dictionaries, we can
        # use dict.get for all input objects
        def get_attribute(self, obj, key, default):
            return obj.get(key, default)
  7. Use Context for environment-aware serialization

    dev

    If a field needs access to environmental information (e.g., current user, related objects) during (de)serialization, use the experimental marshmallow.experimental.context.Context class. You can set the context using Context as a context manager, and retrieve it within fields using Context[Type].get().

    import typing
    from dataclasses import dataclass
    from marshmallow import Schema, fields
    from marshmallow.experimental.context import Context
    
    @dataclass
    class User:
        name: str
    
    @dataclass
    class Blog:
        title: str
        author: User
    
    class ContextDict(typing.TypedDict):
        blog: Blog
    
    class UserSchema(Schema):
        name = fields.String()
    
        is_author = fields.Function(
            lambda user: user == Context[ContextDict].get()["blog"].author
        )
        likes_bikes = fields.Method("writes_about_bikes")
    
        def writes_about_bikes(self, user: User) -> bool:
            return "bicycle" in Context[ContextDict].get()["blog"].title.lower()
    
    # Usage
    user = User("Freddie Mercury", "fred@queen.com")
    blog = Blog("Bicycle Blog", author=user)
    
    schema = UserSchema()
    with Context({"blog": blog}):
        result = schema.dump(user)
        print(result["is_author"])  # => True
        print(result["likes_bikes"])  # => True
  8. Upgrade fields.Function from func to serialize (Marshmallow 2.3+)

    dev

    When upgrading to Marshmallow 2.3 or later, the func parameter in fields.Function is renamed to serialize. While func remains available for backwards-compatibility in version 2.x, it will be removed in Marshmallow 3.0.

    If you do not provide the serialize parameter, you must use the deserialize parameter by name to avoid ambiguity.

    # YES
    lowername = fields.Function(serialize=lambda obj: obj.name.lower())
    # or
    lowername = fields.Function(lambda obj: obj.name.lower())
    
    # NO
    lowername = fields.Function(func=lambda obj: obj.name.lower())
    
    # Using deserialize explicitly
    lowername = fields.Function(deserialize=lambda name: name.lower())
  9. Use the new Context API in marshmallow 4.0

    dev

    Passing context directly to Schema <marshmallow.schema.Schema> classes is removed in marshmallow 4.0. Instead, use contextvars.ContextVar to pass context to fields, validators, and processing methods. Marshmallow 4 provides an experimental Context <marshmallow.experimental.context.Context> wrapper around contextvars.ContextVar to simplify setting and retrieving context.

    import typing
    
    from marshmallow import Schema, fields
    from marshmallow.experimental.context import Context
    
    
    class UserContext(typing.TypedDict):
        suffix: str
    
    
    UserSchemaContext = Context[UserContext]
    
    
    class UserSchema(Schema):
        name_suffixed = fields.Function(
            lambda obj: obj["name"] + UserSchemaContext.get()["suffix"]
        )
    
    
    with UserSchemaContext({"suffix": "bar"}):
        UserSchema().dump({"name": "foo"})
        # {'name_suffixed': 'foobar'}
  10. Rename pass_many to pass_collection in decorators

    dev

    In marshmallow 4.0, the pass_many argument in the following decorators has been renamed to pass_collection:

    • pre_load <marshmallow.decorators.pre_load>
    • post_load <marshmallow.decorators.post_load>
    • pre_dump <marshmallow.decorators.pre_dump>
    • post_dump <marshmallow.decorators.post_dump>

    Behavior remains unchanged.

    from marshmallow import Schema, fields, post_load
    
    
    class MySchema(Schema):
        name = fields.Str()
    
        @post_dump(pass_collection=True)
        def post_dump(self, data, many, **kwargs): ...
  11. Use load_default and dump_default instead of missing and default

    dev

    In marshmallow >=3.13, the missing and default parameters for fields have been renamed to load_default and dump_default, respectively. These are passed to the field constructor as keyword arguments.

    from marshmallow import Schema, fields
    
    # Before 3.13
    class MySchema(Schema):
        name = fields.Str(missing="Monty")
        age = fields.Int(default=42)
    
    # 3.13 and later
    class MySchema(Schema):
        name = fields.Str(load_default="Monty")
        age = fields.Int(dump_default=42)
  12. Customize field-level error messages

    dev

    You can customize error messages for specific fields in two ways:

    1. Per-field customization: Pass an error_messages dictionary to a specific field instance (e.g., fields.Str(required=True, error_messages=...)).
    2. Global field defaults: Modify Field.default_error_messages to change the default message for all instances of a field type across your application.

    Example of both approaches:

    from marshmallow import Schema, fields
    
    # Set a global default for all fields
    fields.Field.default_error_messages["required"] = "You missed something!"
    
    class ArtistSchema(Schema):
        name = fields.Str(required=True)
        # Override for a specific field
        label = fields.Str(required=True, error_messages={"required": "Label missing."})
    
    print(ArtistSchema().validate({}))
    # {'label': ['Label missing.'], 'name': ['You missed something!']}