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: Laravel, 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? 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

Ready? Let’s get started!

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

First things first make sure you have a virtual Python environment at your disposal. You may use pipenv, pyenv or the venv module in Python 3.

For this project I’d like to use a native virtual environment:

python3 -m venv VenvDjango

Once created move inside the new folder and activate the environment:

cd VenvDjango/ && source bin/activate

Now let’s start the project by creating a new directory:

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

and pull in the dependencies: install Django and Django REST framework by running:

pip install django djangorestframework

When the installation ends 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! YOUR_CODE_DIR will be something like VenvDjango. Here’s the complete path for me: /home/valentino/VenvDjango/django-drf-react-quickstart/project

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 for testing 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?? 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:

pip install coverage

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.

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": [
        "@babel/preset-env", "@babel/preset-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