Nesting Schemas¶
Schemas can be nested to represent relationships between objects (e.g. foreign key relationships). For example, a Blog
may have an author represented by a User object.
import datetime as dt
class User(object):
def __init__(self, name, email):
self.name = name
self.email = email
self.created_at = dt.datetime.now()
self.friends = []
self.employer = None
class Blog(object):
def __init__(self, title, author):
self.title = title
self.author = author # A User object
Use a Nested
field to represent the relationship, passing in a nested schema class.
from marshmallow import Schema, fields, pprint
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
created_at = fields.DateTime()
class BlogSchema(Schema):
title = fields.String()
author = fields.Nested(UserSchema)
The serialized blog will have the nested user representation.
user = User(name="Monty", email="monty@python.org")
blog = Blog(title="Something Completely Different", author=user)
result = BlogSchema().dump(blog)
pprint(result)
# {'title': u'Something Completely Different',
# 'author': {'name': u'Monty',
# 'email': u'monty@python.org',
# 'created_at': '2014-08-17T14:58:57.600623+00:00'}}
Note
If the field is a collection of nested objects, you must set many=True
.
collaborators = fields.Nested(UserSchema, many=True)
Specifying Which Fields to Nest¶
You can explicitly specify which attributes of the nested objects you want to serialize with the only
argument.
class BlogSchema2(Schema):
title = fields.String()
author = fields.Nested(UserSchema, only=["email"])
schema = BlogSchema2()
result = schema.dump(blog)
pprint(result)
# {
# 'title': u'Something Completely Different',
# 'author': {'email': u'monty@python.org'}
# }
You can represent the attributes of deeply nested objects using dot delimiters.
class SiteSchema(Schema):
blog = fields.Nested(BlogSchema2)
schema = SiteSchema(only=['blog.author.email'])
result = schema.dump(site)
pprint(result)
# {
# 'blog': {
# 'author': {'email': u'monty@python.org'}
# }
# }
You can replace nested data with a single value (or flat list of values if many=True
) using the Pluck
field.
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.Pluck('self', 'name', many=True)
# ... create ``user`` ...
serialized_data = UserSchema().dump(user)
pprint(serialized_data)
# {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": ["Mike", "Joe"]
# }
deserialized_data = UserSchema().load(result)
pprint(deserialized_data)
# {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": [{"name": "Mike"}, {"name": "Joe"}]
# }
You can also exclude fields by passing in an exclude
list. This argument also allows representing the attributes of deeply nested objects using dot delimiters.
Partial Loading¶
Nested schemas also inherit the partial
parameter of the parent load
call.
class UserSchemaStrict(Schema):
name = fields.String(required=True)
email = fields.Email()
created_at = fields.DateTime(required=True)
class BlogSchemaStrict(Schema):
title = fields.String(required=True)
author = fields.Nested(UserSchemaStrict, required=True)
schema = BlogSchemaStrict()
blog = {'title': 'Something Completely Different', 'author': {}}
result = schema.load(blog, partial=True)
pprint(result)
# {'author': {}, 'title': 'Something Completely Different'}
You can specify a subset of the fields to allow partial loading using dot delimiters.
author = {'name': 'Monty'}
blog = {'title': 'Something Completely Different', 'author': author}
result = schema.load(blog, partial=('title', 'author.created_at'))
pprint(result)
# {'author': {'name': 'Monty'}, 'title': 'Something Completely Different'}
Two-way Nesting¶
If you have two objects that nest each other, you can refer to a nested schema by its class name. This allows you to nest Schemas that have not yet been defined.
For example, a representation of an Author
model might include the books that have a foreign-key (many-to-one) relationship to it. Correspondingly, a representation of a Book
will include its author representation.
class AuthorSchema(Schema):
# Make sure to use the 'only' or 'exclude' params
# to avoid infinite recursion
books = fields.Nested('BookSchema', many=True, exclude=('author', ))
class Meta:
fields = ('id', 'name', 'books')
class BookSchema(Schema):
author = fields.Nested(AuthorSchema, only=('id', 'name'))
class Meta:
fields = ('id', 'title', 'author')
from marshmallow import pprint
from mymodels import Author, Book
author = Author(name='William Faulkner')
book = Book(title='As I Lay Dying', author=author)
book_result = BookSchema().dump(book)
pprint(book_result, indent=2)
# {
# "id": 124,
# "title": "As I Lay Dying",
# "author": {
# "id": 8,
# "name": "William Faulkner"
# }
# }
author_result = AuthorSchema().dump(author)
pprint(author_result, indent=2)
# {
# "id": 8,
# "name": "William Faulkner",
# "books": [
# {
# "id": 124,
# "title": "As I Lay Dying"
# }
# ]
# }
Note
If you need to, you can also pass the full, module-qualified path to fields.Nested
.
books = fields.Nested('path.to.BookSchema',
many=True, exclude=('author', ))
Nesting A Schema Within Itself¶
If the object to be marshalled has a relationship to an object of the same type, you can nest the Schema
within itself by passing "self"
(with quotes) to the Nested
constructor.
class UserSchema(Schema):
name = fields.String()
email = fields.Email()
friends = fields.Nested('self', many=True)
# Use the 'exclude' argument to avoid infinite recursion
employer = fields.Nested('self', exclude=('employer', ), default=None)
user = User("Steve", 'steve@example.com')
user.friends.append(User("Mike", 'mike@example.com'))
user.friends.append(User('Joe', 'joe@example.com'))
user.employer = User('Dirk', 'dirk@example.com')
result = UserSchema().dump(user)
pprint(result, indent=2)
# {
# "name": "Steve",
# "email": "steve@example.com",
# "friends": [
# {
# "name": "Mike",
# "email": "mike@example.com",
# "friends": [],
# "employer": null
# },
# {
# "name": "Joe",
# "email": "joe@example.com",
# "friends": [],
# "employer": null
# }
# ],
# "employer": {
# "name": "Dirk",
# "email": "dirk@example.com",
# "friends": []
# }
# }
Next Steps¶
- Want to create your own field type? See the Custom Fields page.
- Need to add schema-level validation, post-processing, or error handling behavior? See the Extending Schemas page.
- For example applications using marshmallow, check out the Examples page.