Tutorial: Django REST with React (Django 2.0 and a sprinkle of testing)

A practical (opinionated) introduction to using Django REST with React. Featuring Django 2.0!

Tutorial: Django REST with React (Django 2.0)

There is no shortage of web frameworks these days.

Want to build an API? Here it is:

Rails, Node.js and Koa 2, Phoenix, you name it.

But here’s the reality: the client wants a prototype quickly. What should I do?

I pick a web framework that:

  • lets me write less code
  • lets me ship a MVP as soon as possibile
  • provides a solid foundation for extending the project

And trust me, Django is great when it comes to development speed.

But how to create a simple Django REST API?

How to structure a Django project with React?

Fear not, we’ll find out together!

Django REST with React: what you will learn

In the following tutorial you’ll learn:

  • how to build a simple Django REST API
  • how to structure a Django project with React

What we will build?

I love marketing automation (I’m building a lightweight Drip clone for a client).

And in this project we’ll build a simple API for listing and storing leads.

Django REST with React: requirements

To follow along with the tutorial you should have:

  • a basic understanding of Python and Django.
  • a basic understanding of Javascript ES6 and React.
  • a newer version of Node.js installed on your system
  • pipenv installed on your system

Ready? Let’s get started!

Django REST with React: setting up a Python virtual environment, and the project

What’s your favorite tool for managing Python virtual environments?

I felt in love with pipenv. I like its resemblance with NPM.

Let’s start the project by creating a new directory:

mkdir django-drf-react-quickstart && cd $_

Next up create a new virtual environment by running:

pipenv --three

Now it’s time to pull our dependencies in.

Install Django and Django REST framework by running:

pipenv install django djangorestframework

When the installation ends spawn a shell within the virtual environment by running:

pipenv shell

At this point you’re ready to create a new Django project:

django-admin startproject project

Now we can start building our first Django app: a simple API for listing and storing leads.

Django REST with React: bulding a Django application

A Django project consists of many applications. Each application should ideally do one thing.

Django applications are modular and reusable. For example: I can create a leads application for creating and listing leads.

If another project needs the same app I can install leads from the package manager and that’s all.

I suggest reading How to write reusable apps and watching DjangoCon 2008: Reusable Apps to learn about app best practices.

To create a new application in Django you would run:

django-admin startapp app_name

To create the leads app move inside the project folder:

cd project

and initialize the app:

django-admin startapp leads

Note: I’m assuming you’re in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ while running the above command!

You’ll see a new directory called leads inside ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/.

Now let’s tell Django how to use the new app.

Open up ./project/settings.pyand add the app in INSTALLED_APPS:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', # add the leads app
]

So far so good!

In the next section we’ll add our first model.

Django REST with React: creating a Django model

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

With the app in place it’s time to create our first model.

A model is an object representing your table’s data.

Almost every web framework has the concept of models. Django makes no exception.

A Django model may have one or more field: each field is a column in your table.

Before moving forward let’s define our requirements for the lead application.

First we need a Lead model.

Since I’m collecting leads I can think of a Lead model made of the following fields:

  • a name
  • an email
  • a message

(Feel free to add extra fields! Like phone for example).

Let’s not forget a timestamp field as well! Django does not add a created_at column by default.

Well.

Open up ./leads/models.pyand create the Lead model:

from django.db import models

class Lead(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField()
    message = models.CharField(max_length=300)
    created_at = models.DateTimeField(auto_now_add=True)

A quick note about models: take your time to check the Django fields documentation.

When planning a model try to choose the most appropriate fields for your use case.

And with the model in place let’s create a migration by running:

python manage.py makemigrations leads

and finally migrate the database with:

python manage.py migrate

Great! In the next sections we’ll talk about serializers and views. But first a note about testing.

Django REST with React: a sprinkle of testing

At this point you may wonder “Valentino, how about testing the application??”

Rather than annoying you with a TDD tutorial I’ll give you some tips instead.

I’ve seen a ton of Django tutorials starting like so:

class SomeModelModelTest(TestCase):
    def setUp(self):
        SomeModel.objects.create(
            name=fake.name(),
            email=fake.email(),
            phone=fake.phone_number(),
            message=fake.text(),
            source=fake.url()
        )

    def test_save_model(self):
        saved_models = SomeModel.objects.count()
        self.assertEqual(saved_models, 2)

Don’t do that. There’s no point in testing neither a vanilla Django model nor the Django ORM.

Here’s a good starting point about what to test in Django:

  • do not test Django built-in code (models, views, etc)
  • do not test Python built-in functions

To recap: do not test what is already tested!

So what should I test??

Any custom method needs a test!

Have you added a custom method to a Django model? Test it!

Do you have a custom view? Test it!

But how do I know what to test exactly?

Do yourself a favour. Install coverage:

pipenv install coverage --dev

Then, every time you add some code to your application run coverage with:

coverage run --source='.' manage.py test

and generate the report:

coverage html

Look for ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/htmlcov/index.htmland open the file in your browser.

You’ll see exactly what to test.

If you prefer seeing the report on the command line run:

coverage report

Wait, are you still there? I’m impressed!

Hold tight, in the next section we’ll take a look at serializers!

Django REST with React: Django REST serializers

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

What is serialization?

What is a Django REST serializer?

Serialization is the act of transforming an object into another data format.

After transforming an object we can save it to a file or send it through the network.

Why serialization is necessary?

Think of a Django model: it’s a Python class. How do you render a Python class to JSON in a browser?

With a Django REST serializer!

A serializer works the other way around too: it converts JSON to objects.

This way you can:

  • display Django models in a browser by converting them to JSON
  • make CRUD request with a JSON payload to the API

To recap: a Django REST serializer is mandatory for operating on models through the API.

Create a new file named ./leads/serializers.py. The LeadSerializer takes our Lead model and some fields:

from rest_framework import serializers
from leads.models import Lead

class LeadSerializer(serializers.ModelSerializer):
    class Meta:
        model = Lead
        fields = ('id', 'name', 'email', 'message')

As you can see we’re subclassing ModelSerializer.

A ModelSerializer in Django REST is like a ModelForm.

It is suitable whenever you want to closely map a Model to a Serializer.

Besides defining each field explicitly you can also map all the model fields:

from rest_framework import serializers
from leads.models import Lead

class LeadSerializer(serializers.ModelSerializer):
    class Meta:
        model = Lead
        fields = '__all__'

Save and close the file. We’re one step closer to completing the application.

In the next sections we’ll take a look at views and urls.

Django REST with React: setting up the controll… ehm the views

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

Coming from other frameworks you may find surprising that Django has no controllers.

The controller encapsulates the logic for processing requests and returning responses. In the traditional MVC architecture there is the Model, the View, and the Controller.

Example of MVC frameworks are Rails, Phoenix, Laravel.

Django is a MVT framework. That is, Model – View – Template. The View takes care of the request/response lifecycle.

There are many types of views in Django: function views, class based views, and generic views.

Although some developers prefer function views in place of class based views I am a big fan of the latter.

When I pick Django it’s because I value development speed, DRY, less code.

I see no point in writing views by hand when there’s already a set of sane defaults.

Here’s my rule of thumb:

Use function views only if the time spent customizing a generic view is more than the time spent writing the view by hand.

As with plain Django, in Django REST framework there are many ways for writing views:

For the scope of this tutorial I will use generic API views. The goal is to write less code.

Our simple app should:

  • list a collection of models
  • create new objects in the database

By taking a look at the generic API views documentation we can see that there’s a view for listing and creating models.

It’s ListCreateAPIView.

The ListCreateAPIView takes a queryset and a serializer_class.

Open up ./leads/views.pyand create the view:

from leads.models import Lead
from leads.serializers import LeadSerializer
from rest_framework import generics

class LeadListCreate(generics.ListCreateAPIView):
    queryset = Lead.objects.all()
    serializer_class = LeadSerializer

That is. With 3 lines of code we created a view for handling GET and POST requests.

What’s missing now? URL mapping! In other words we should map URLs to views.

How? Head over to the next section …

Django REST with React: setting up the rout… ehm the urls

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

Coming from Rails, Phoenix, or Laravel you may find surprising that there are no route configuration in Django.

Even though DRF comes with a resourceful router, the simplest way to map a URL to a view is URL mapping.

Our goal is to wire up LeadListCreate to api/lead/.

In other words we want to make GET and POST requests to api/lead/ for listing and creating models.

To configure the URL mapping include the app urls in ./project/urls.py:

from django.urls import path, include

urlpatterns = [
    path('', include('leads.urls')),
]

next up create a new file named ./leads/urls.py.

In this file we’ll wire up LeadListCreate to api/lead/:

from django.urls import path
from . import views

urlpatterns = [
    path('api/lead/', views.LeadListCreate.as_view() ),
]

Finally let’s enable rest_frameworkin INSTALLED_APPS.

Open up ./project/settings.pyand add the app in INSTALLED_APPS:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', 
    'rest_framework' # enable rest framework
]

Now you should be able to run a sanity check with:

python manage.py runserver

Head over http://127.0.0.1:8000/api/lead/ and you’ll see the browsable API

Django REST with React: setting up the rout... ehm the urls
Django REST browsable API

While you’re at it try to create some data through the builtin form.

In the next section we’ll learn how to seed the database in Django.

Django REST with React: seeding the database

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

You can use Django fixtures to populate the database.

Fixtures are useful when you want to give a demo with some data in the frontend.

Create a new directory named ./leads/fixtures.

Then create a new file named ./leads/fixtures/leads.jsonwith the following JSON:

[
    {
        "model": "leads.lead",
        "pk": 1,
        "fields": {
            "name": "Armin",
            "email": "something@gmail.com",
            "message": "I am looking for a Javascript mentor",
            "created_at": "2018-02-14 00:00:00"
        }
    },
    {
        "model": "leads.lead",
        "pk": 2,
        "fields": {
            "name": "Tom",
            "email": "tomsomething@gmail.com",
            "message": "I want to talk about a Python project",
            "created_at": "2018-01-14 00:00:00"
        }
    }
]

Save and close the file, then load the fixture with:

python manage.py loaddata leads

That’s all!

In the next sections we’ll implement a simple React frontend (finally!).

Django REST with React: Django and React together

Django REST with React: Django and React together

A lot of fellow Python developers struggle with a simple question.

How to glue Django and React together?

Should React router take over the routing?

Should React mount a component in each Django template? (If you want to lose sanity).

I’d say “it depends”.

It depends on how much Javascript do you need.

But how much Javascript is too much? (I don’t know, just kidding!)

Jokes aside there are many ways for setting up a Django project with React.

I see the following patterns (which are common to almost every web framework):

  1. React in its own “frontend” Django app: load a single HTML template and let React manage the frontend (difficulty: medium)
  2. Django REST as a standalone API + React as a standalone SPA (difficulty: hard, it involves JWT for authentication)
  3. Mix and match: mini React apps inside Django templates (difficulty: simple)

And here are my advices.

If you’re just starting out with Django REST and React avoid the option 2.

Go for option number 1 (React in its own “frontend” Django app) if:

  • you’re building an app-like website
  • the interface has lot of user interactions/AJAX
  • you’re fine with Session based authentication
  • there are no SEO concerns
  • you’re fine with React Router

Keeping React closer to Django makes easier to reason about authentication and other stuff.

You can exploit the Django builtin authentication for registering and logging in users.

Use the good ol’ Session authentication and do not worry too much about tokens and JWT.

(I will cover authentication and permission in an upcoming tutorial).

Go for option number 3 (mini React apps inside Django templates) if:

  • the website doesn’t need much Javascript
  • you must take care of SEO

We’ll explore the approach 1 in the next section.

By the way there are still situations in which using React for the entire frontend is not an option.

In that case you can always throw in Vue without feeling guilty.

Django REST with React: setting up React and webpack

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward!!

The sweet spot for Django and React is Django REST framework for providing API endpoints.

With React in its own app called “frontend”.

We already know how to create a Django app so let’s do it again:

django-admin startapp frontend

You’ll see a new directory called frontend inside ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/.

Here’s how the project will look now:

$ ls -1
frontend
leads
manage.py
project

Let’s also prepare a directory structure for holding the React components:

mkdir -p ./frontend/src/components

and the static files:

mkdir -p ./frontend/{static,templates}/frontend

Next up we’ll set up React, webpack 4 and Babel.

A quick note before moving forward.

Since the frontend is a standalone app it could make sense to install webpack and friends in ./frontend.

But making our intentions explicit is not a bad idea.

Would be better if I put package.jsonin the main directory?

Every developer could look at the repo and say “ok, there’s React and webpack stuff there”.

What do you think? Let’s do it.

Assuming you’re in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/move to the upper directory:

cd ..

and initialize the environment:

npm init -y

Next up install webpack and webpack cli with:

npm i webpack webpack-cli --save-dev

Now open up package.json and configure the scripts:

"scripts": {
  "dev": "webpack --mode development ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js",
  "build": "webpack --mode production ./project/frontend/src/index.js --output ./project/frontend/static/frontend/main.js"
}

Close the file and save it.

For learning more about webpack 4 check out Webpack 4 Tutorial: from 0 Conf to Production Mode

Now let’s install babel for transpiling our code:

npm i babel-core babel-loader babel-preset-env babel-preset-react babel-plugin-transform-class-properties --save-dev

babel-plugin-transform-class-properties is necessary for using ES6 class static properties

Pull in React and prop-types:

npm i react react-dom prop-types --save-dev

Configure Babel by creating a new file named .babelrcinside the project folder:

{
    "presets": [
        "env", "react"
    ],
    "plugins": [
        "transform-class-properties"
    ]
}

And finally create a new file named webpack.config.jsfor configuring babel-loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader"
        }
      }
    ]
  }
};

Now we’re ready to roll! (Welcome to frontend in 2018).

Django REST with React: the React frontend

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward

Let’s see how we can wire up our React frontend.

First things first create a view in ./frontend/views.py:

from django.shortcuts import render

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

It is an humble function view for returning our template.

Then create the template in ./frontend/templates/frontend/index.html:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
  <title>Django DRF - React : Quickstart - Valentino G. - www.valentinog.com</title>
</head>

<body>
  <section class="section">
    <div class="container">
          <div id="app" class="columns"><!-- React --></div>
    </div>
  </section>
</body>

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

</html>

As you can see the template will call frontend/main.jswhich is our webpack bundle.

Psst! Bulma is my favourite CSS framework for rapid prototyping!

Configure the new URL mapping to include the frontend in ./project/urls.py:

urlpatterns = [
    path('', include('leads.urls')),
    path('', include('frontend.urls')),
]

next up create a new file named ./frontend/urls.py.

In this file we’ll wire up the view to our root:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index ),
]

Finally enable the frontend app in ./project/settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'leads', 
    'rest_framework',
    'frontend' # enable the frontend app
]

At this point you can give it a shot with:

python manage.py runserver

and you will see nothing at http://127.0.0.1:8000/ because there’s one missing piece: React.

For creating a simple React frontend we will build 3 components:

  1. App, the “mother” component
  2. Dataprovider, a stateful component for fetching data (featuring render props!)
  3. Table, a stateless component for displaying data

The App component

It’s the main component for attaching React to <div id="app"> </div>.

Create a new file named ./frontend/src/components/App.js:

import React from "react";
import ReactDOM from "react-dom";
import DataProvider from "./DataProvider";
import Table from "./Table";

const App = () => (
  <DataProvider endpoint="api/lead/" 
                render={data => <Table data={data} />} />
);

const wrapper = document.getElementById("app");

wrapper ? ReactDOM.render(<App />, wrapper) : null;

The DataProvider component

A stateful component for fetching data (featuring React render props!)

Create a new file named ./frontend/src/components/DataProvider.js:

import React, { Component } from "react";
import PropTypes from "prop-types";

class DataProvider extends Component {
  static propTypes = {
    endpoint: PropTypes.string.isRequired,
    render: PropTypes.func.isRequired
  };

  state = {
      data: [],
      loaded: false,
      placeholder: "Loading..."
    };

  componentDidMount() {
    fetch(this.props.endpoint)
      .then(response => {
        if (response.status !== 200) {
          return this.setState({ placeholder: "Something went wrong" });
        }
        return response.json();
      })
      .then(data => this.setState({ data: data, loaded: true }));
  }

  render() {
    const { data, loaded, placeholder } = this.state;
    return loaded ? this.props.render(data) : <p>{placeholder}</p>;
  }
}

export default DataProvider;

The Table component

A stateless component for displaying data within a table.

Create a new file named ./frontend/src/components/Table.js:

import React from "react";
import PropTypes from "prop-types";
import key from "weak-key";


const Table = ({ data }) =>
  !data.length ? (
    <p>Nothing to show</p>
  ) : (
    <div className="column">
      <h2 className="subtitle">
        Showing <strong>{data.length} items</strong>
      </h2>
      <table className="table is-striped">
        <thead>
          <tr>
            {Object.entries(data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
          </tr>
        </thead>
        <tbody>
          {data.map(el => (
            <tr key={el.id}>
              {Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );

Table.propTypes = {
  data: PropTypes.array.isRequired
};

export default Table;

The component generates rows dinamically so we need to rely on an external package for React keys id.

As pointed out by Bartosz  using shortid might not be optimal.

A better alternative to shortid for React is weak-key:

npm i weak-key --save-dev

Finally create the entry point for webpack in ./frontend/src/index.js:

import App from "./components/App";

Save and close the file.

At this point we’re ready to test things out.

Run webpack with:

npm run dev

start the development server:

python manage.py runserver

and head over http://127.0.0.1:8000/

If you see “Something went wrong” make sure to migrate and populate your database:

python manage.py migrate && python manage.py loaddata leads

and start the server again.

Surprise!

You should finally see your fantastic React app ehm… table!

Django REST with React: the React frontend

How does it look?

It’s pretty simple. But it works!

Django REST with React: testing the frontend

NOTE: make sure you’re still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward

A React form for creating new leads is the next natural step for our project.

While doing so we’ll introduce some testing for the frontend.

If you want to be serious about testing go grab a copy of Obey the testing goat.

It will teach you a lot (with Selenium).

But I won’t use Selenium here: our tool of the trade is Cypress.

To install Cypress run:

npm i cypress --save-dev

Give it a minute and you’re ready to go.

Now move in the frontend app:

cd ./frontend

and open up Cypress:

../../node_modules/.bin/cypress open

The reason for this song and dance is because Cypress creates a directory for itself.

In fact you will see a new directory named cypressin the frontend app.

Do not worry though, we’ll configure an NPM script for running Cypress in the correct folder.

(You can stop Cypress for now).

Keeping UI tests inside the frontend folder makes a lot of sense but feel free to initialize Cypress anywhere else.

Before moving to the next step configure the base url in cypress.json(you can find the file in the frontend folder):

{
  "baseUrl": "http://127.0.0.1:8000"
}

and while you’re there let’s configure two NPM scripts:

  1. one for flushing our Django database
  2. another one for running Cypress

Open up package.json and configure the scripts:

"scripts": {
  "flush": "pipenv run python ./project/manage.py flush --no-input",
  "e2e": "cypress open --project ./project/frontend/",
  //...
},

Now let’s write a simple test for our form.

I’m still in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/frontend

Create a new file named ./cypress/integration/app_spec.js.

The test should visit our site, find the form and fill it:

describe("Django REST framework / React quickstart app", () => {
  const lead = {
    name: "Armin",
    email: "some-email@gmail.com",
    message: "I am looking for a React tutor"
  };

  before(() => {
    cy.exec("npm run dev");
    cy.exec("npm run flush");
  });

  it("should be able to fill a web form", () => {
    cy.visit("/");

    cy
      .get('input[name="name"]')
      .type(lead.name)
      .should("have.value", lead.name);

    cy
      .get('input[name="email"]')
      .type(lead.email)
      .should("have.value", lead.email);

    cy
      .get('textarea[name="message"]')
      .type(lead.message)
      .should("have.value", lead.message);

    cy.get("form").submit();
  });
  // more tests here
});

Now let’s run the server from another terminal:

# Run inside ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/
pipenv run python manage.py runserver

Start Cypress:

npm run e2e

and finally click app_spec.js in the Integration Tests section.

Django REST framework React - Cypress testing

The test will fail because there’s no form! Of course…

Let’s create one!

Why not starting the Django server with cy.exec? It is an anti-pattern: do not start your backend web server from within Cypress

Django REST with React: building a React form

NOTE: make sure you’re in ~/YOUR_CODE_DIR/django-drf-react-quickstart/project/ before moving forward

We should create the form to make our test pass.

Create a new file named ./frontend/src/components/Form.js:

import React, { Component } from "react";
import PropTypes from "prop-types";

class Form extends Component {
  static propTypes = {
    endpoint: PropTypes.string.isRequired
  };

  state = {
    name: "",
    email: "",
    message: ""
  };

  handleChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  handleSubmit = e => {
    e.preventDefault();
    const { name, email, message } = this.state;
    const lead = { name, email, message };
    const conf = {
      method: "post",
      body: JSON.stringify(lead),
      headers: new Headers({ "Content-Type": "application/json" })
    };
    fetch(this.props.endpoint, conf).then(response => console.log(response));
  };

  render() {
    const { name, email, message } = this.state;
    return (
      <div className="column">
        <form onSubmit={this.handleSubmit}>
          <div className="field">
            <label className="label">Name</label>
            <div className="control">
              <input
                className="input"
                type="text"
                name="name"
                onChange={this.handleChange}
                value={name}
                required
              />
            </div>
          </div>
          <div className="field">
            <label className="label">Email</label>
            <div className="control">
              <input
                className="input"
                type="email"
                name="email"
                onChange={this.handleChange}
                value={email}
                required
              />
            </div>
          </div>
          <div className="field">
            <label className="label">Message</label>
            <div className="control">
              <textarea
                className="textarea"
                type="text"
                name="message"
                onChange={this.handleChange}
                value={message}
                required
              />
            </div>
          </div>
          <div className="control">
            <button type="submit" className="button is-info">
              Send message
            </button>
          </div>
        </form>
      </div>
    );
  }
}

export default Form;

The form does not clear itself but it’s easy to implement a reset function. Do it!

Next up modify ./frontend/src/components/App.jsto include the new component:

import React from "react";
import ReactDOM from "react-dom";
import DataProvider from "./DataProvider";
import Table from "./Table";
import Form from "./Form";

const App = () => (
  <React.Fragment>
    <DataProvider endpoint="api/lead/" 
                  render={data => <Table data={data} />} />
    <Form endpoint="api/lead/" />
  </React.Fragment>
);

const wrapper = document.getElementById("app");

wrapper ? ReactDOM.render(<App />, wrapper) : null;

And before running the test suite again let’s add another little check.

Test that the user can see the table:

// insert after the first "it" block in ./cypress/integration/app_spec.js
  it("should be able to see the table", () => {
    cy.visit("/");
    cy.get("tr").contains(`${lead.name}${lead.email}${lead.message}`);
  });

Make sure both Cypress and the server are still open and run the test again…

it should pass!

Django REST with React: testing a React form with Cypress

It’s fantastic. Isn’t it?

Granted, the app is quite simple and based on a contrived example.

But it’s a nice starting point for getting started with React and Django REST.

At this point you’ve completed the barebone of a simple Django / React project.

You’ve learned how to:

  • build a simple Django REST API
  • structure a Django project with React
  • connect React to the Django REST API

Feel free to experiment by adding more functionalities to the project (authentication).

Django REST with React: wrapping up

There are tons of frameworks for building APIs.

But what to do when the client wants a prototype quickly?

You pick Django.

Why?

Django is DRY. DRY equals less code. And less code equals less bugs.

Testing is a breeze with the integrated testing tools.

Authentication is a pleasure with the built-in Django auth.

Django:

  • lets you write less code
  • lets you ship a MVP fast
  • provides a solid foundation for extending the project

And last but not least Django REST will make your life easier.

Give it a try.

Thanks for reading and happy coding!

Django REST with React: resources

A Github repo for the tutorial => django-drf-react-quickstart

Just starting out with Django and Django REST? Make sure to check out:

Write an API for Almost Anything by Charlotte Mays

Django REST official doc

Already proficient with Django? Check out DjangoCon 2008: Reusable Apps

The Django Book

William S. Vincent has another nice tutorial for getting started with Django REST and React

Thanks to Cory Zue for the precious feedback

Valentino Gagliardi

Valentino Gagliardi

Consultant, Developer Coach. Are you stuck on a project? Let's talk!
Valentino Gagliardi

47 Replies to “Tutorial: Django REST with React (Django 2.0 and a sprinkle of testing)”

    1. You can use create-react-app as long as you copy the resulting bundle in ./project/frontend/static/frontend/main.js.

      If you’re wondering why I’m not using create-react-app for building a stand-alone frontend…

      The point of the article is: keep React closer to Django so you can use the built-in authentication. The bundle must be in the same context of the Django site.

      Cheers

  1. Hello Valentino,

    My name is David Zuluaga I am from Colombia, In this moment i search blogs about how implement Django+React, your article is very interesting and and it helps a lot to understand the interaction between Django + React. My question is, if react allows SPA(Single page application) why as register one Led, this Led not visualizated inmediatly in the page?

    Thank you

  2. Hi, thanks for the tutorial. It’s going great until I run the webpack.
    I keep getting an npm error that says I may need an appropriate loader to handle this file type?
    I’ve tried looking into troubleshooting it with https://github.com/babel/babel-loader/issues/173
    but to no avail.

    Here is my error:

    npm run dev

    Hash: fa1cab67453c98c9b965
    Version: webpack 4.1.1
    Time: 138ms
    Built at: 3/15/2018 4:12:24 PM
    Asset Size Chunks Chunk Names
    main.js 3.94 KiB main [emitted] main
    Entrypoint main = main.js
    [./project/frontend/src/index.js] 35 bytes {main} [built]
    + 1 hidden module

    ERROR in ./project/frontend/src/components/App.js
    Module parse failed: Unexpected token (7:2)
    You may need an appropriate loader to handle this file type.
    |
    | const App = () => (
    | } />
    | );
    @ ./project/frontend/src/index.js 1:0-35
    npm ERR! code ELIFECYCLE
    npm ERR! errno 2
    npm ERR! django-ognajd@1.0.0 dev: `webpack –mode development ./project/frontend/src/index.js –output ./project/frontend/static/frontend/main.js`
    npm ERR! Exit status 2
    npm ERR!
    npm ERR! Failed at the django-ognajd@1.0.0 dev script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    Thanks

      1. @Nico
        Make sure you use all npm commad in upper project folder, and place .babelrc and webpack.config.js in upper project folder, that is “django-drf-react-quickstart” folder in Valentino’s case

        1. Yes, this is the fix for me!
          Make sure .babelrc and webpack.config.js are in the top folder (`django-drf-react-quickstart`).
          Thanks @Dino !!

      1. Hi Valentino,

        Sysadmin here, ex-php dev, fell in love with django but need spa behaviors for my management apps

        A friend of mine is javascript guru and bets for using React with node (instead django), but I am 200% pragmatic and prefer dev speed instead bells and whistles (angular/react/vue/whatever)

        There is any way to build django spa apps using some kind for Form/ModelForm builder/validations instead build and validating them manually?
        Thanks in advance!

  3. I was wondering if you have any particular reason for using cypress over selenium. I see that for this prototype you picked cypress, but I wonder if in a big app you would opt for selenium.

    1. Hi Tiago

      I’ve a couple of medium-to-bigger projects right now. I’m using Cypress for UI tests in project A and TestCafè for UI tests in project B. Jest for unit testing React components.

      On the other hand I use the classic TestCase for unit testing Django and the REST framework APIClient for testing the API.

      I understand the convenience of using Python for both UI and unit tests but I’m more than happier with Cypress and TestCafè.

  4. Reasons were provided to use Option 1 and 3 but no reasons/situations for using option 2

    Could someone describe reasons/situations for using option 2

    1. React in its own “frontend” Django app: load a single HTML template and let React manage the frontend (difficulty: medium)
    2. Django REST as a standalone API + React as a standalone SPA (difficulty: hard, it involves JWT for authentication)
    3. Mix and match: mini React apps inside Django templates (difficulty: simple)

    1. Let me explain it as far as I could, from what I understand is that you build the frontend (React) and backend (DRF) as separate projects. Then, after both of them are built, your React will consume your DRF’s API, and you can host these apps on different places, e.g. DRF on Heroku and React on Github Pages. But then, you will have extra work to do as you’ve got to handle the auth using JWT

  5. I keep running into this.

    λ npm run dev

    > Idcweb@1.0.0 dev c:\DjangoProjects(2)\IDCWEB\Idcweb
    > webpack –mode development ./Idcweb/frontend/src/index.js –output ./Idcweb/frontend/static/frontend/main.js

    Hash: 1f9dbea14444f1fa2b5a
    Version: webpack 4.2.0
    Time: 49ms
    Built at: 2018-3-28 12:23:01

    ERROR in Entry module not found: Error: Can’t resolve ‘./Idcweb/frontend/src/index.js’ in ‘c:\DjangoProjects(2)\IDCWEB\Idcweb’
    npm ERR! code ELIFECYCLE
    npm ERR! errno 2
    npm ERR! Idcweb@1.0.0 dev: `webpack –mode development ./Idcweb/frontend/src/index.js –output ./Idcweb/frontend/static/frontend/main.js`
    npm ERR! Exit status 2
    npm ERR!
    npm ERR! Failed at the Idcweb@1.0.0 dev script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in:

    Can someone help me out?

  6. Thanks for the writeup! How would you approach having the table re-render every time a lead is added without requiring a page refresh?

  7. Anyone know why I am getting this error…seems like I can’t go past this. I have checked every step on this tutorial haven’t missed anything. The “Kiguru” is in place of “project” in this tutorial…

    Hash: b7c4b2e94bb45ae9bdaa
    Version: webpack 4.5.0
    Time: 139ms
    Built at: 4/16/2018 4:55:27 PM
    Asset Size Chunks Chunk Names
    main.js 3.98 KiB main [emitted] main
    Entrypoint main = main.js
    [./Kiguru/frontend/src/index.js] 81 bytes {main} [built]
    + 1 hidden module

    ERROR in ./Kiguru/frontend/src/components/App.js
    Module parse failed: Unexpected token (12:2)
    You may need an appropriate loader to handle this file type.
    |
    | const App = () => (
    | } />
    | );
    @ ./Kiguru/frontend/src/index.js 7:0-35

    npm ERR! Darwin 17.5.0
    npm ERR! argv “/usr/local/bin/node” “/usr/local/bin/npm” “run” “dev”
    npm ERR! node v6.11.1
    npm ERR! npm v3.10.10
    npm ERR! code ELIFECYCLE
    npm ERR! networking_web@1.0.0 dev: `webpack –mode development ./Kiguru/frontend/src/index.js –output ./Kiguru/frontend/static/frontend/main.js`
    npm ERR! Exit status 2

  8. This is an amazing tutorial, thanks a lot!
    From the tons of other tutorials (especially in Django-React how-tos) yours is the most clear and detailed.
    You saved my day 🙂

  9. I like your style but I believe I need to use API option 2 with standalone front and back ends. I am already comfortable with React and am off to Django REST main website. Is standalone React/DRF too boilerplate to write up API option 2?

  10. Excellent tutorial.

    I got a question, in real world is a good practice wrapp a react application inside a Django application like you did?

  11. Thanks for the wonderful tutorial. You are a very good teacher. Would love to do some projects together with you. Cheers.

  12. Hi Valentino, How are you?
    Thanks so much for this well written tutorial. No wonder it is linked at the official DRF page. I have one doubt though 🙂
    At the tutorial you’re always pointing to use Django session, which I completely agree with you, but when posting the form, you didn’t use CSRF tokens. So, how to use CSRF tokens?

    Seems to me, (please, correct me, if I’m wrong), that when making posts in Django, without CSRF, session won’t work.

    So for every request (GET, POST, PUT…) will I have to send the CSRF token?
    The “{% csrf_token%}” must be defined in the basic index template?

    Once again, thank you so much for you time in writing this tutorial.

  13. Hello i get this error when i try to use: npm run dev

    > bin@1.0.0 dev /home/ubuntu/Schreibtisch/env_python3/bin
    > webpack –mode development ./project/frontend/src/index.js –output ./project/frontend/static/frontend/main.js

    /usr/bin/env: »node“: File or Directory not found

    npm ERR! Linux 4.13.0-36-generic
    npm ERR! argv “/usr/bin/nodejs” “/usr/bin/npm” “run-script” “dev”
    npm ERR! node v4.2.6
    npm ERR! npm v3.5.2
    npm ERR! file sh
    npm ERR! code ELIFECYCLE
    npm ERR! errno ENOENT
    npm ERR! syscall spawn
    npm ERR! bin@1.0.0 dev: `webpack –mode development ./project/frontend/src/index.js –output ./project/frontend/static/frontend/main.js`
    npm ERR! spawn ENOENT
    npm ERR!
    npm ERR! Failed at the bin@1.0.0 dev script ‘webpack –mode development ./project/frontend/src/index.js –output ./project/frontend/static/frontend/main.js’.
    npm ERR! Make sure you have the latest version of node.js and npm installed.
    npm ERR! If you do, this is most likely a problem with the bin package,
    npm ERR! not with npm itself.
    npm ERR! Tell the author that this fails on your system:
    npm ERR! webpack –mode development ./project/frontend/src/index.js –output ./project/frontend/static/frontend/main.js
    npm ERR! You can get information on how to open an issue for this project with:
    npm ERR! npm bugs bin
    npm ERR! Or if that isn’t available, you can get their info via:
    npm ERR! npm owner ls bin
    npm ERR! There is likely additional logging output above.

    npm ERR! Please include the following file with any support request:
    npm ERR! /home/ubuntu/Schreibtisch/env_python3/bin/npm-debug.log

  14. Are there any tutorials you can suggest for the Django + React learning and creating a complete web application using it?

  15. Hello Valentino

    I’ve noticed that you use:

    in your examples. This is a very very bad idea. If you’re callign `uuid` in render methods, it will generate a new unique key every time the component re-renders. This results in React having to remove the old node from DOM and then having to create a new one. And in worst-case scenarios unmount and remount components.

    You should be using an identifier of the item you’re looping over, for example the id of the item stored in a databse. If you’re unable to do that (e.g. don’t have one) you should pre-generate the id on client, for each of the items (for example in lifecycle, when populating the items array).

      1. That’s def. better, but in this case you could just use the key of the object you are itterating over, remember that the key needs to be unique only in given loop 🙂

  16. Thank you for your post!

    I am considering about refreshing page after introducing changes in js files automatically. How can I achieve that functionality using your solution?

    1. go to package.json and add a –watch to the dev line like this:

      “dev”: “webpack –watch –mode …

      After that just start the serve withr: npm run dev

  17. I have been building everything with javascript for the past 5 years, Backend to Frontend. A couple of months ago i decided to venture into Django land and have not regretted it. I just started building a small project management tool and this was the perfect tutorial for me to get started.

    cheers!
    //K

  18. Not too sure if anyone else was having the same problem.

    since the post suggested to use weak-key, shortid should probably be excluded from the code above.

    it caused me trouble when I run “npm run dev”.

  19. Can I implement option 3 (mini React apps inside Django templates) based on the approach explained? How to generate different JS bundles for many django apps throughout project structure ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.