Using Django as Ember.js back-end

Ember.js is moving a lot, but since the adoption of JSONAPI for the default "wire protocol" with back-ends it's easier to keep up.

I have had good luck using the Django Rest Framework JSON API library on top of Django Rest Framework.

This post will try to document how to get things working and forget about your back-end for a while. I'm going to follow the Django Tutorial schema, with an important addition of adding a related_name to the ForeignKey field.

from django.db import models

class Question(models.Model):  
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):  
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="choices")
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Do the usual makemigrations and migrate then also register the models in the Admin.

Install Django Rest Framework and the aforementioned JSON API adapter. Also install django-filter. In the, make sure you add rest_framework.

pip install django-rest-framework  
pip install git+https:[email protected]evelop --upgrade # FIXME once 2.0 is properly released it should be enough  
pip install django-filter  

Now configure the DRF like so:

    #'PAGE_SIZE': 10,
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
# FIXME do permission and authentication as you see fit.
    'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler',
    'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',

# this is optional but very useful
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),


JSON_API_FORMAT_KEYS = 'dasherize'  

Now create a new file:

from .models import Question, Choice  
from rest_framework_json_api import serializers, relations  
from rest_framework import viewsets, views, response

import django_filters  
from rest_framework import filters

class QuestionSerializer(serializers.ModelSerializer):  
    choices = relations.ResourceRelatedField(read_only=True, many=True)
    class Meta:
        model = Question

class ChoiceSerializer(serializers.ModelSerializer):  
    class Meta:
        model = Choice

class QuestionViewSet(viewsets.ModelViewSet):  
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

class ChoiceViewSet(viewsets.ModelViewSet):  
    queryset = Choice.objects.all()
    serializer_class = ChoiceSerializer

# this is should plural and dasherized names
ROUTES = {  
    'questions': QuestionViewSet,
    'choices': ChoiceViewSet

Then in the global

from rest_framework import routers  
router = routers.DefaultRouter(trailing_slash=False)  
from import ROUTES  
for key, viewset in ROUTES.items():  
    router.register(key, viewset)

urlpatterns = [  
    url(r'^api/v1/', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

The only Ember change you have to do is generate an application adapter with this content:

import DS from 'ember-data';

export default DS.JSONAPIAdapter.extend({  
    namespace: 'api/v1',
    isInvalid(status, headers, payload) {
        return status === 422 || status === 400;

Now run the Django dev server, and don't forget to use ember serve --proxy to ensure your Ember app can easily make Ajax requests there.