Sideload JSON API resources in Django

I’m a big proponent of using a framework to reduce the time required to make something useful. Occasionally, using a framework means that developers must explore to understand what is possible. This truth is even more evident when there are layers of extensions to achieve a desired result. For instance, College Conductor is driving an EmberJS app with a JSON API powered by Django. The API uses the excellent Django REST Framework and the DRF JSON API extension. DRF JSON API translates from vanilla DRF API serialized output to JSON API flavored output. In this post, I’ll cover my exploration of the DRF JSON API source code to find out how to load extra model resources in a single API request.

The JSON API specification describes how to include extra resources using a Compound Document. Compound Documents are the mechanism to serialize extra resources in a single request. The benefit of doing this is saving at least one HTTP request (with all of its associated delay) and possibly more if the client app is inefficient with resource requests. Unfortunately, DRF JSON API does not explain how to make a Compound Document (a fact I hope to correct with this Pull Request). For now, that means that users must scour the source. What follows is an example that can save you from source code spelunking.

Let’s suppose that you would like to make a quest game. In your game, you have a quest and you send a knight on your quest. There is a very small amount of information about your knight so it would be great to send that data along with the quest.

We can begin with the non-sideloaded versions of the two serializers.

from rest_framework_json_api import serializers

from game.models import Knight, Quest


class KnightSerializer(serializers.ModelSerializer):
    class Meta:
        model = Knight
        fields = (
            'id',
            'name',
            'strength',
            'dexterity',
            'charisma',
        )


class QuestSerializer(serializers.ModelSerializer):
    class Meta:
        model = Quest
        fields = (
            'id',
            'title',
            'reward',
            'knight',
        )

Let’s assume that the Quest.knight is a foreign key to a Knight. In this version of our QuestSerializer, DRF JSON API will serialize the associated knight’s ID without the data that we want.

Now, we can transform the QuestSerializer in a couple of ways. First, the code:

class QuestSerializer(serializers.ModelSerializer):
    included_serializers = {
        'knight': KnightSerializer,
    }

    class Meta:
        model = Quest
        fields = (
            'id',
            'title',
            'reward',
            'knight',
        )

    class JSONAPIMeta:
        included_resources = ['knight']

Hopefully, the two changes are hard to miss. This new serializer informs DRF JSON API of what we want the API to include via the included_resources meta attribute. We must also tell the serializer how to serialize the knight. The how is accomplished by the included_serializers dictionary. These settings are enough to make DRF JSON API include both the quest and knight data in a single request. Neat!

Before we part, I must sadly note that this is how this should work in theory. There is currently a bug (#291) that is fixed by Pull Request #307. When that is merged and released, you’ll be able to use sideloaded resources in your Django based JSON API.