Custom serialization¶
New in version 0.17.0.
Changed in version 1.0.0b1: Transitioned from function-based serialization to class-based serialization.
Flask-Restless provides serialization and deserialization that work with the
JSON API specification. If you wish to have more control over the way
instances of your models are converted to Python dictionary representations,
you can specify custom serialization by providing it to
APIManager.create_api()
via the serializer_class
keyword argument.
Similarly, to provide a deserializer that converts a Python dictionary
representation to an instance of your model, use the deserializer_class
keyword argument. However, if you provide a serializer that fails to produce
resource objects that satisfy the JSON API specification, your client will
receive non-compliant responses!
Your serializer classes must be a subclass of DefaultSerializer
and
can override the serialize()
and
serialize_many()
methods to provide custom
serialization. These methods take an instance or instances as input and return
a dictionary representing a JSON API document. Each also accepts an only
keyword argument, indicating the sparse fieldsets requested by the client:
from flask_restless import DefaultSerializer
class MySerializer(DefaultSerializer):
def serialize(self, instance, only=None):
super_serialize = super(DefaultSerializer, self).serialize
document = super_serialize(instance, only=only)
# Make changes to the document here...
...
return document
def serialize_many(self, instances, only=None):
super_serialize = super(DefaultSerializer, self).serialize_many
document = super_serialize(instances, only=only)
# Make changes to the document here...
...
return document
instance
is an instance of a SQLAlchemy model, instances
is a list of
instances, and the only
argument is a list; only the fields (that is, the
attributes and relationships) whose names appear as strings in only should
appear in the returned dictionary. The only exception is that the keys 'id'
and 'type'
must always appear, regardless of whether they appear in
only. The function must return a dictionary representation of the resource
object.
Flask-Restless also provides functional access to the default serialization,
via the simple_serialize()
and simple_serialize_many()
functions,
which return the result of the built-in default serialization.
For deserialization, define your custom deserialization class like this:
from flask_restless import DefaultDeserializer
class MyDeserializer(DefaultDeserializer):
def deserialize(self, document):
return Person(...)
document
is a dictionary representation of the complete incoming JSON API
document, where the data
element contains the primary resource object or
objects. The function must return an instance of the model that has the
requested fields. If you override the constructor, it must take two positional
arguments, session and model.
Your code can raise a SerializationException
when overriding the
DefaultSerializer.serialize()
method, and similarly a
DeserializationException
in the
DefaultDeserializer.deserialize()
method; Flask-Restless will
automatically catch those exceptions and format a JSON API error response.
If you wish to collect multiple exceptions (for example, if several fields of a
resource provided to the deserialize()
method fail
validation) you can raise a MultipleExceptions
exception, providing a
list of other serialization or deserialization exceptions at instantiation
time.
Note
If you wish to write your own serialization functions, we strongly suggest using a Python object serialization library instead of writing your own serialization functions. This is also likely a better approach than specifying which columns to include or exclude (Inclusion of related resources) or preprocessors and postprocessors (Request preprocessors and postprocessors).
For example, if you create schema for your database models using Marshmallow, then you use that library’s built-in serialization functions as follows:
class PersonSchema(Schema):
id = fields.Integer()
name = fields.String()
def make_object(self, data):
return Person(**data)
class PersonSerializer(DefaultSerializer):
def serialize(self, instance, only=None):
person_schema = PersonSchema(only=only)
return person_schema.dump(instance).data
def serialize_many(self, instances, only=None):
person_schema = PersonSchema(many=True, only=only)
return person_schema.dump(instances).data
class PersonDeserializer(DefaultDeserializer):
def deserialize(self, document):
person_schema = PersonSchema()
return person_schema.load(instance).data
# # JSON API doesn't currently allow bulk creation of resources. When
# # it does, either in the specification or in an extension, this is
# # how you would implement it.
# def deserialize_many(self, document):
# person_schema = PersonSchema(many=True)
# return person_schema.load(instance).data
manager = APIManager(app, session=session)
manager.create_api(Person, methods=['GET', 'POST'],
serializer_class=PersonSerializer,
deserializer_class=PersonDeserializer)
For a complete version of this example, see the
examples/server_configurations/custom_serialization.py
module in the
source distribution, or view it online.
Per-model serialization¶
The correct serialization function will be used for each type of SQLAlchemy
model for which you invoke APIManager.create_api()
. For example, if you
create two APIs, one for Person
objects and one for Article
objects,
manager.create_api(Person, serializer=person_serializer)
manager.create_api(Article, serializer=article_serializer)
and then make a request like
GET /api/article/1?include=author HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
then Flask-Restless will use the article_serializer
function to serialize
the primary data (that is, the top-level data
element in the response
document) and the person_serializer
to serialize the included Person
resource.