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.