Build a CRUD Todo app with Django and React/Redux

Tutorial for Beginners

Koji Mochizuki
Jun 29 · 10 min read
Photo by Anete Lūsiņa on Unsplash

In this tutorial, we will learn how to build a CRUD Todo application with Django REST framework for the backend, React and Redux for the frontend.

At the end of this tutorial, we will have the application that looks like this:

CRUD Todo App

Table of Contents

  • Setting up Django
  • Setting up React
  • Getting data from the API and displaying the list
  • Creating Form and adding a new Todo
  • Creating a Header
  • Removing Todos
  • Editing Todos

Setting up Django

Creating a virtual environment with Pipenv

First, we will create the folder for the project and navigate into it:

$ mkdir django-react-todo
$ cd django-react-todo

Let’s create a virtual environment by running this command:

$ pipenv --python 3

If you don’t have Pipenv installed yet, please install it by running this command:

$ pip install pipenv

We will install the packages we need:

$ pipenv install django djangorestframework

Creating a new project and some apps

In this tutorial, we will create a project named “todocrud”. We can leave out the extra folder which is automatically created by adding a dot to the end of the command and running:

$ django-admin startproject todocrud .

Next, we will create two apps. One is for the backend, the other is for the frontend:

$ python manage.py startapp todos
$ python manage.py startapp frontend

We will open the settings.py file in the project directory, and configure to use the apps we created and Django REST framework:

We can specify the output format of a date and time by including DATETIME_FORMAT in a configuration dictionary named REST_FRAMEWORK.

Let’s apply migrations and start the development server:

$ python manage.py migrate
$ python manage.py runserver

Visit http://127.0.0.1:8000/ with your browser. If you see the page a rocket taking off, it worked well!

Writing backend’s modules

First, we will create a simple model. Open the models.py file and write the following code:

Next, we will build a simple model-backed API using REST framework. Let’s create a new folder named api and create new files __init__.py, serializers.py, views.py and urls.py in it:

todos/
api/
__init__.py
serializers.py
urls.py
views.py

Since the api is a module, we need to include __init__.py file.

Let’s define the API representation in the serializers.py file:

The ModelSerializer class will create fields that correspond to the Model fields.

Next, we will define the view behavior in the api/views.py file:

We will finally write the URL configuration using Routers:

We use three arguments to the register() method, but the third argument is not required.

Writing frontend’s modules

In the frontend, all we have to do is write simple views and URL patterns.

Open the frontend/views.py file and create the two views:

We will create the frontend/index.html file later. Don’t worry about it now.

Add a new urls.py file to the same directory and create the URL conf:

As you can see above, the index view is for the index page and the TodoDetailView is called when we request a specific object.

Wire up the URLs

We will include the frontend’s and backend’s URLs to the project’s URLconf:

Although the path for the Django admin site is left, we are not going to use it in this tutorial.

As a final part of this chapter, we will make a new migration file and apply changes to our databases by running the commands below:

$ python manage.py makemigrations
$ python manage.py migrate

Setting up React

Creating directories

First of all, let’s create all of the directories we need:

$ mkdir -p ./frontend/src/{components,actions,reducers}
$ mkdir -p ./frontend/{static,templates}/frontend

The above command should create the directories as follows:

frontend/
src/
actions/
components/
reducers/
static/
frontend/
templates/
frontend/

Installing packages

We need to create a package.json file by running the following command before installing packages:

$ npm init -y

In order to use npm, Node.js needs to be installed.

Then, let’s install all the packages we use with npm command:

$ npm i -D webpack webpack-cli
$ npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties
$ npm i react react-dom react-router-dom
$ npm i redux react-redux redux-thunk redux-devtools-extension
$ npm i redux-form
$ npm i axios
$ npm i lodash

Creating config files

Add a file named .babelrc to the root directory and configure Babel:

We can use Async/Await with Babel by writing as above.

Secondary, add a file named webpack.config.js to the same directory and write a configuration for webpack:

Additionally, we need to rewrite the "scripts” property of the package.json file:

Two new scripts have been defined. We can run scripts with npm run dev for development or npm run build for production. When these scripts are run, webpack bundles modules and output the main.js file.

Creating principal files

Let’s create three principal files and render the first word.

We will create a file named index.js that is called first when we run the React application:

Next, we will create a file named App.js that is a parent component:

We will finally create a template file named index.html that is specified in the views.py file:

In this tutorial, we will use Semantic UI as a CSS framework.

Place the wrapper for rendering the App component and the bundled script into the <body> tag.

Checking the display

Let’s see whether it is displayed correctly.

Open another terminal and run the script:

$ npm run dev

The main.js file should be generated in the static/frontend directory.

Then, start the dev server and visit http://127.0.0.1:8000/:

$ python manage.py runserver

If the word “ToDoCRUD” is displayed, everything is going well so far :)

Getting data from the API and displaying the list

It is time to use Redux. We will create Actions, Reducers and Store.

Actions

Let’s define all the type properties in advance. Add a new file named types.js into the src/actions directory:

In order to create actions, we need to define Action Creators. Add a new file named todos.js into the src/actions directory:

Reducers

Reducers specify how the application’s state changes in response to actions sent to the store.

That is the role of Reducers. Add a new file named todos.js into the src/reducers directory and write a child reducer:

Lodash is a JavaScript utility library. It is not a requirement, but it can cut down on development time and make your codebase smaller.

Let’s create the parent reducer to put together every child reducer using combineReducers(). Add a new file named index.js into the src/reducers directory:

In order to use redux-form, we need to include its reducer in the combineReducers function.

Store

The Store is an object to hold the state of our application. In addition, we will use the recommended middleware Redux Thunk to write async logic that interacts with the store. Let’s create a new file named store.js in the src directory:

Use of Redux DevTools is optional, but it is very useful because it visualizes the state changes of Redux. I will omit how to use it here, but it is highly recommended.

Components

First, create a new folder named todos in the components directory. And then, add a new file named TodoList.js into the folder we created:

We use Semantic UI list to decorate the list.

The connect() function connects this component to the store. It accepts mapStateToProps as the first argument, Action Creators as the second argument. We will be able to use the store state as Props by specifying mapStateToProps.

We will create a new file named Dashboard.js in the same directory. It is just a container for TodoList and a form we will create in the next chapter:

Open the App.js file and update as follows:

The Provider makes the store available to the component nested inside of it.

Checking the display

First, visit http://127.0.0.1:8000/api/todos/ and create several objects. And then, visit http://127.0.0.1:8000/.

You should see a simple list of the objects you created. Did it work?

Creating Form and adding a new Todo

Actions

Open the actions/todos.js file, and add a new action creator:

Dispatching reset('formName') clears our form after we submission succeeds. We will specify the form name later in the Form component.

Reducers

Open the reducers/todos.js file, and add a new action to the reducer:

Components

Let’s create a Form component. We will create a Form separately as a reusable component so that it can also be used for editing. Create a new file named TodoForm.js in the components/todos directory:

The tutorial would be lengthy, so I will leave out how to use Redux Form. To understand how the Redux Form works, it is a good idea to try to customize your form referring to the documentation.

'todoForm' is the name of this form. That is what we used in the action creator addTodo.

When we click in the textbox and then remove the focus, it displays a validation error, so specify touchOnBlur: false to disable it.

Next, let’s create a component for adding new todos. Create a new file named TodoCreate.js in the components/todos directory:

All we have to do is render the TodoForm. By setting destroyOnUnmount to false, we can disable that the Redux Form automatically destroys a form state in the Redux store when the component is unmounted. It is for displaying the form state in an editing form.

If we don’t need to specify a mapStateToProps function, set null into connect().

Let’s view and test the form. Open the Dashboard.js file, and update as follows:

Creating a Header

Let’s take a break and create a header. Create a new folder named layout, and then add a new file name Header.js into it:

Open the App.js file and nest the Header component:

In this tutorial, the header is just an ornament.

Removing Todos

First, we will create a history object using the history package. We can use it for changing the current location. Create a new file named history.js in the frontend/src directory, and write the code below:

Actions

Open the actions/todos.js file, and add two new action creators:

We have created getTodo to get a specific object and deleteTodo to delete the object.

We are going to create a modal window for confirmation of deletion later. The history.push('/') method automatically takes us from the modal window to the index page after removing an object.

Reducers

Open the reducers/todos.js file, and add the actions to the reducer:

The GET_TODO action is the same as the ADD_TODO action, so we only need to set the case. For the DELETE_TODO action, use Lodash again as a shortcut.

Components

Let’s create the modal window I just mentioned. We will have it that looks like this:

The modal window for confirmation of the deletion

Create a new file named Modal.js in the components/layout directory, and write as follows:

To render the Modal component outside the DOM hierarchy of the parent component, create a portal using createPortal(). The first argument is the renderable child element and the second argument is the DOM element to render.

And then, open the index.html file and add a container for the Modal inside the <body> tag:

Next, we will create a new component TodoDelete.js in the components/todos directory:

The code is a bit long, but it is not so difficult. Define the helper functions that display the content and the action buttons on the modal window. Then, pass them as Props to the Modal component. onDismiss is set to return to the index page when the dim part of the modal window is clicked.

We can retrieve the data from its own props by specifying ownProps as the second argument to mapStateToProps.

Let’s open the TodoList.js file and put a delete button:

Finally, we need to configure routing using React Router. Open the App.js file and configure as follows:

The reason for using Router instead of BrowserRouter is explained in the REACT TRAINING document as follows:

The most common use-case for using the low-level <Router> is to synchronize a custom history with a state management lib like Redux or Mobx. Note that this is not required to use state management libs alongside React Router, it’s only for deep integration.

The exact parameter specified in Route returns a route only if the path exactly matches the current URL.

That concludes this chapter. Try deleting some objects and see if it works.

Editing Todos

This is the last chapter. We are almost done, so let’s keep going!

Actions

Open the actions/todos.js file, and add a new action creator:

Reducers

Open the reducers/todos.js file, and add the action to the reducer:

Components

Create a new component TodoEdit.js in the components/todos directory:

Specify an object in initialValues. We can get only the value of task using the _.pick function of Lodash. In addition, set enableReinitialize to true so that we can also get the value when the page is reloaded. Pass these optional properties to the TodoForm.

Open the TodoList.js file and update <a className='header'>{todo.task}</a> as follows:

Let’s add the new component to the App.js file:

Finally, let’s change the text of the button on the edit form from “Add” to “Update”. Open the TodoForm.js file and update as follows:

Now, click on any task on the index page and try editing:

The edit form

You should have been able to change the value from the form.

This tutorial ends here. The source code of this app is available on GitHub. Thank you for reading!

TechNest

Tutorials and tips for developers.

Koji Mochizuki

Written by

Full-Stack Web Dev | JS Node Python | Blockchain enthusiast | Twitter account: @kjmczk

TechNest

TechNest

Tutorials and tips for developers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade