Django Rest Framework addons to integrate with Ember.js


Keywords
EmberJS, Django, REST
License
BSD-3-Clause
Install
pip install emberdrf==0.1.7

Documentation

Warning - Ember Data and JSON API

Since the creation of this addon, Ember Data has released a stable version and is officially supporting JSON API. Ember DRF does not current support JSON API and there are no immediate plans to implement support for it. Currently this addon is best suited for integrating DRF with Ember Data versions prior to 1.13.

Overview

This project aims to create a python package that makes it simple to build an API that is compatible with Ember Data out of the box. While initially the user will still be required to configure their urls and serializers in a certain way for them to work properly, the goal is to make this as pluggable as possible.

Installation

The module is available on pypi as emberdrf and can be installed using pip:

pip install emberdrf

Django Rest Framework compatability

This module will only work with version 3 or higher of django rest framework.

pip install djangorestframework

Please make sure you that you have DRF 3.0 properly installed before trying to use ember_drf.

How to Use

The following is a complete list of steps to configure your api to work with Ember-Data out of the box.

1. Serializers

You must expose only subclasses of SideloadSerializer from your viewsets in order for them to work properly with Ember-Data.

# serializers.py
from rest_framework.serializers import ModelSerializer
from ember_drf.serializers import SideloadSerializer
from my_app.models import Fruit

class FruitSerializer(ModelSerializer):
    class Meta:
        model = Fruit
        fields = ('basket', 'tree')

class FruitSideloadSerializer(SideloadSerializer):
    class Meta:
        base_serializer = FruitSerializer
        sideloads = [
            (Basket, BasketSerializer)
        ]
# viewsets.py
class FruitViewset(ModelViewSet):
    model = Fruit
    serializer_class = FruitSideloadSerializer

Assuming that basket and tree are both one-to-many relationships, the Ember-Data model could look like this:

export default DS.Model.extend({
    basket: DS.belongsTo('basket'),
    tree: DS.belongsTo('tree', {async: true}) // async because no sideloads
});

SideloadSerializer also supports Embedded Records using the standard rest_framework syntax. Information on using EmbeddedRecords in Ember can be found here.

JSON Keys

The json keys used are derived from the model name using the same methodology as in ember. They can be overridden by setting the base_key property on the rest_framework.serializer.ModelSerializer subclass:

class FruitSerializer(ModelSerializer):
    class Meta:
        base_key = 'tasty_fruit'

2. Renderers/Parsers

Ember-Data offers two built-in serializers, DS.EmberJSONSerializer and DS.ActiveRecordJSONSerializer. DS.EmberJSONSerializers is enabled by default. However, you may want to use ActiveRecordJSONSerializer as it conforms more closely to the standard DRF output (e.g. underscored urls and properties). Either way you will need to configure DRF to use the renderers and parsers provided by ember_drf.

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        # Choose one:
        'ember_drf.renderers.ActiveModelJSONRenderer',
        'ember_drf.renderers.EmberJSONRenderer',

        # leave this
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_PARSER_CLASSES': (
        # Choose one:
        'ember_drf.parsers.ActiveModelJSONParser',
        'ember_drf.parsers.EmberJSONParser',

        # leave this
        'rest_framework.parsers.MultiPartParser',
    ),
    # ... include other REST_FRAMEWORK settings as needed
}

Additional details on how to use renders can be found here

3. Urls

Ember will not by default append a trailing slash to urls. You can turn off trailing slashes in django. It is probably less intrusive to make this change on the Ember side however:

# adapters/application.js
import DS from "ember-data";
// alternatively, use DS.Adapter if you do not want to use ActiveModel
export default DS.ActiveModelAdapter.extend({
    buildURL: function(type, id, record){
        return this._super(type, id, record) + '/';
    }
});

4. CSRF

This is really outside of the scope of this project; however, in most cases you will need to configure the Ember adapter to include a CSRF Token. The CSRF_TOKEN_VARIABLE is probably most easily set by embedding the csrf token into the index.html template.

# adapters/application.js
import DS from "ember-data";
// alternatively, use DS.Adapter if you do not want to use ActiveModel
export default DS.ActiveModelAdapter.extend({
    headers: {
        'X-CSRFToken': CSRF_TOKEN_VARIABLE
    }
});

5. Coalescing Find Requests

Ember-Data has a feature where it can coalesce multiple single find requets into a single query. To support this you will need to add ember_drf.filters.CoallesceIDsFilterBackend to your FILTER_BACKENDS settings.

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': (
        'rest_framework.filters.DjangoFilterBackend',
        'ember_drf.filters.CoallesceIDsFilterBackend'
    )
    # ... include other REST_FRAMEWORK settings as needed
}

6. Errors Formatting

Ember-Data expects errors to be nested in an errors key and to have a 422 status code. To accomplish this ember_drf provides a custom exception handler. To implement it you need to change the EXCEPTION_HANDLER setting as follows:

# settings.py
REST_FRAMEWORK = {
  'EXCEPTION_HANDLER': 'ember_drf.views.exception_handler'
    # ... include other REST_FRAMEWORK settings as needed
}