Pycon X - Florence

5 May 2019

Valentino Gagliardi

JavaScript Developer and Trainer


I 💚 Python

Such kid! Much wow!

The plan

  1. introduction to decoupling
  2. a (re)introduction to Django REST
  3. setting up a JavaScript front-end
  4. focus on testing
  5. conclusion and resources

Goal of this talk

Give you an overview/ guideline for structuring a Django project with React (or any JavaScript framework/library).

Outline challenges and tradeoffs of a decoupled approach.

By the end of the talk you should be able to configure a new Django REST project with a front-end library for interacting with the API.

1. Introduction

A bit of backstory

What is "decoupling"?

Exposing data and contents "outside" Django templates, with the help of a REST API.

Decoupling Django means exposing your models as JSON.


Why decoupling?

Because of flexibility... Back-end and front-end developers can work in isolation. Also, no need to fight build tools.

But a decoupled approach can give a lot of headaches! Code duplication, more testing, more developers.


A decoupled approach imposes challenges!

  • search engine optimization (SSR or die)
  • authentication
  • logic duplication (errors and form validation)
  • more testing
  • more developers

When to decouple?

These are good signs that you will benefit from decoupling:

  • lot of  JS-driven interactions
  • you're building a mobile app
  • you're building a dashboard for internal use

When NOT to decouple

These are good signs that you don't need to decouple:

  • the application has little or no JS-driven interactions
  • you're concerned about SEO
  • you want to decouple just because everyone is doing the same ("Medium" driven development)

2. Django REST framework

Django REST framework

Django REST framework is a Django application for building REST APIs on top of Django.

pip install djangorestframework

Django models

But first ... Django models!

from django.db import models
some other model here!

class Link(models.Model):
    title = models.TextField(max_length=200, unique=True)
    url = models.URLField(max_length=250, unique=True)
    tags = models.ManyToManyField(Tag)
    created_at = models.DateField(auto_now_add=True)

DRF serializers

Serializers are a layer between Django models and the outside world (they mirror Django forms).

from rest_framework import serializers
from .models import Link
class LinkSerializer(serializers.ModelSerializer):

    class Meta:
        model = Link
        fields = ('title', 'url', 'tags')
    tags = serializers.StringRelatedField(many=True)
        fields = '__all__' # Don't do that

DRF function views

Views in DRF can be written as plain functions.

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Link
from .serializers import LinkSerializer

def link_list(request):
    links = Link.objects.all()
    serializer = LinkSerializer(links, many=True)
    return Response(

DRF generic views

But even better with Generic Views.

from rest_framework.generics import ListAPIView
from .models import Link
from .serializers import LinkSerializer

class LinkList(ListAPIView):
    serializer_class = LinkSerializer
    queryset = Link.objects.all()

Wiring up the urls

After creating a serializer and a view we can expose the API endpoint.

# links/

from django.urls import path
from .views import LinkList
PREFIX = 'api'

urlpatterns = [
    path(f'{PREFIX}/link/', LinkList.as_view()),

The result

3. Setting up the frontend

What is React?

React is a front-end library for building composable, reusable, user interfaces.

npm i react react-dom

React components

import React from "react";

function Form(props) {
  return <form onSubmit={props.onSubmit}>

<Form onSubmit={handleSubmit}>

React components are ... JavaScript functions!

Let's glue things together

And now comes the hard part. Many ways for pairing up a JavaScript frontend to a Django REST API.

A closer look at two practical approaches ...

#1 create-react-app

Approach #1: A CRA React app in app_name/

#1 create-react-app

<!-- How about these? -->

<script src="/static/js/2.3310f33a.chunk.js"></script>
<script src="/static/js/main.c82d5dba.chunk.js"></script>

<!-- We do need these instead -->
{% load static %}
<script src="{% static "links/js/2.3310f33a.chunk.js" %}"></script>
<script src="{% static "links/js/main.c82d5dba.chunk.js" %}"></script>

But when running npm run build you get these files:

#2 Custom webpack project

Approach #2: a custom webpack project in app_name/

#2 Custom webpack project

We have control over the bundle path! Ok for development:

// webpack.config.js
const path = require("path");

module.exports = {
  entry: "./index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, 
  module: {
        // other stuff

#2 Custom webpack project

<!-- Seems good! -->

{% load static %}
<script src="{% static "links/js/main.js" %}"></script>

But now ... how about splitChunks and code splitting?

Don't forget to expose a view!

def index(request):
    return render(request, 'custom_webpack_conf_2/index.html')

#2 Custom webpack project

With splitChunks you can aggressively split your bundle. Ending up with a lot of files ...

How do we inject these files into our Django template?

#2.1 django-webpack-loader


Learning from others

Maybe we can build a "django-webpacker"?

The winner is ...

Fully decoupled front-end! Best of both worlds, you don't fight tools.

Don't forget to enable django-cors-headers!

Do you even authenticate?

Authentication plays an important role in a decoupled setup.

  • good ol' Django session authentication
  • token based authentication (JWT)
pip install djangorestframework_simplejwt

4. Testing

Testing the front-end

A lot of options for testing the front-end:

  • Selenium, (cannot stub the API, semi-auto waiting)
  • Cypress, (can stub the API, automatic waiting)
  • Splinter, (Selenium abstraction)

UI testing with Cypress

it("Test description (user story)", () => {
      method: "POST", url: "**/api/user/token/", status: 401,
      response: { access: undefined, refresh: undefined }
    const emailInput = 'input[name="username"]';
    const passwordInput = 'input[name="password"]';

Testing the API

A lot of options for testing the API too:

  • APISimpleTestCase: (no db queries)
  • APITestCase: (db is available)
  • APILiveServerTestCase

API testing in DRF

from rest_framework.test import APITestCase
from django.contrib.auth.models import User
from ..models import Link, Tag

class SimpleTest(APITestCase):

    def setUp(self):
        self.path = '/api/link2/' = Link.objects.create(
            title="Some title",
        self.tags = [Tag.objects.create(name=tag)
                     for tag in ['django', 'python']]
        [ for tag in self.tags]

    def test_guest_cannot_see_links(self):
        response = self.client.get(self.path)
        self.assertEqual(response.status_code, 401)

API testing in DRF

# more stuff here

    def test_authenticated_user(self):
        user = User.objects.create_user(username='valentino',
        response = self.client.get(self.path)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(, [{"id": 1,
                                          "title": "Some title",
                                          "url": "someurl",
                                          "tags": ["django", "python"]}])

Demo time!

5. Conclusions and resources


Django REST framework is great!

  • configuration over coding
  • built-in pagination
  • authentication and permissions
  • throttling

Where to go from here

You may want to investigate more advanced topics:

  • DRF router
  • DRF viewset


Get in touch!

Thank you!