handling multiple variants of a marshmallow schema

  • Last Update :
  • Techknowledgy :

However, an example I've encountered where using inheritance was a better fit was when I modelled the arguments for certain graphs I was generating. Here, I wanted general arguments that would be passed into the underlying plotting library, but I wanted the child schemas to refine the schema and use more specific validations:

class PlotSchema(Schema):
   ""
"
Data that can be used to generate a plot
   ""
"
id = f.String(dump_only = True)
type = f.String()
x = f.List(f.Raw())
y = f.List(f.Raw())
text = f.List(f.Raw())
hoverinfo = f.Str()

class TrendSchema(PlotSchema):
   ""
"
Data that can be used to generate a trend plot
   ""
"
x = f.List(f.DateTime())
y = f.List(f.Number())

Suggestion : 2

By default, pre- and post-processing methods receive one object/datum at a time, transparently handling the many parameter passed to the Schema’s dump()/load() method at runtime.,In cases where your pre- and post-processing methods needs to handle the input collection when processing multiple objects, add pass_many=True to the method decorators.,Pre- and post-processing methods may raise a ValidationError. By default, errors will be stored on the "_schema" key in the errors dictionary.,Data pre-processing and post-processing methods can be registered using the pre_load, post_load, pre_dump, and post_dump decorators.

from marshmallow
import Schema, fields, post_load

class UserSchema(Schema):
   name = fields.Str()
slug = fields.Str()

@post_load
def slugify_name(self, in_data, ** kwargs):
   in_data["slug"] = in_data["slug"].lower().strip().replace(" ", "-")
return in_data

schema = UserSchema()
result = schema.load({
   "name": "Steve",
   "slug": "Steve Loria "
})
result["slug"] # => 'steve-loria'
from marshmallow import Schema, fields, pre_load, post_load, post_dump


class BaseSchema(Schema):
    # Custom options
    __envelope__ = {"single": None, "many": None}
    __model__ = User

    def get_envelope_key(self, many):
        """Helper to get the envelope key."""
        key = self.__envelope__["many"] if many else self.__envelope__["single"]
        assert key is not None, "Envelope key undefined"
        return key

    @pre_load(pass_many=True)
    def unwrap_envelope(self, data, many, **kwargs):
        key = self.get_envelope_key(many)
        return data[key]

    @post_dump(pass_many=True)
    def wrap_with_envelope(self, data, many, **kwargs):
        key = self.get_envelope_key(many)
        return {key: data}

    @post_load
    def make_object(self, data, **kwargs):
        return self.__model__(**data)


class UserSchema(BaseSchema):
    __envelope__ = {"single": "user", "many": "users"}
    __model__ = User
    name = fields.Str()
    email = fields.Email()


user_schema = UserSchema()

user = User("Mick", email="mick@stones.org")
user_data = user_schema.dump(user)
# {'user': {'email': 'mick@stones.org', 'name': 'Mick'}}

users = [
    User("Keith", email="keith@stones.org"),
    User("Charlie", email="charlie@stones.org"),
]
users_data = user_schema.dump(users, many=True)
# {'users': [{'email': 'keith@stones.org', 'name': 'Keith'},
#            {'email': 'charlie@stones.org', 'name': 'Charlie'}]}

user_objs = user_schema.load(users_data, many=True)
# [<User(name='Keith Richards')>, <User(name='Charlie Watts')>]
from marshmallow
import Schema, fields, ValidationError, pre_load

class BandSchema(Schema):
   name = fields.Str()

@pre_load
def unwrap_envelope(self, data, ** kwargs):
   if "data" not in data:
   raise ValidationError('Input data must have a "data" key.')
return data["data"]

sch = BandSchema()
try:
sch.load({
   "name": "The Band"
})
except ValidationError as err:
   err.messages
# {
   '_schema': ['Input data must have a "data" key.']
}
from marshmallow
import Schema, fields, ValidationError, pre_load

class BandSchema(Schema):
   name = fields.Str()

@pre_load
def unwrap_envelope(self, data, ** kwargs):
   if "data" not in data:
   raise ValidationError(
      'Input data must have a "data" key.', "_preprocessing"
   )
return data["data"]

sch = BandSchema()
try:
sch.load({
   "name": "The Band"
})
except ValidationError as err:
   err.messages
# {
   '_preprocessing': ['Input data must have a "data" key.']
}
from marshmallow
import Schema, fields, pre_load

# YES
class MySchema(Schema):
   field_a = fields.Field()

@pre_load
def preprocess(self, data, ** kwargs):
   step1_data = self.step1(data)
step2_data = self.step2(step1_data)
return step2_data

def step1(self, data):
   do_step1(data)

# Depends on step1
def step2(self, data):
   do_step2(data)

# NO
class MySchema(Schema):
   field_a = fields.Field()

@pre_load
def step1(self, data, ** kwargs):
   do_step1(data)

# Depends on step1
@pre_load
def step2(self, data, ** kwargs):
   do_step2(data)
from marshmallow
import Schema, fields, validates_schema, ValidationError

class NumberSchema(Schema):
   field_a = fields.Integer()
field_b = fields.Integer()

@validates_schema
def validate_numbers(self, data, ** kwargs):
   if data["field_b"] >= data["field_a"]:
   raise ValidationError("field_a must be greater than field_b")

schema = NumberSchema()
try:
schema.load({
   "field_a": 1,
   "field_b": 2
})
except ValidationError as err:
   err.messages["_schema"]
# => ["field_a must be greater than field_b"]

Suggestion : 3

Schema subclasses can add extra settings to the schema constructor. For example marshmallow-jsonapi addds include_data, which lets you control the amount of data you return for each related resource,Since asking this question, I've done a ton of work using Marshmallow, so hopefully I can explain somewhat.,You end up writing the least code

However, an example I've encountered where using inheritance was a better fit was when I modelled the arguments for certain graphs I was generating. Here, I wanted general arguments that would be passed into the underlying plotting library, but I wanted the child schemas to refine the schema and use more specific validations:

class PlotSchema(Schema): ""
"    Data that can be used to generate a plot    "
""
id = f.String(dump_only = True) type = f.String() x = f.List(f.Raw()) y = f.List(f.Raw()) text = f.List(f.Raw()) hoverinfo = f.Str() class TrendSchema(PlotSchema): ""
"    Data that can be used to generate a trend plot    "
""
x = f.List(f.DateTime()) y = f.List(f.Number())