Full Stack Todo WebApp With React and Python-Django.
Tags: webdev, django, react, api
Published: 2023-05-24 00:33:27

This is a detailed tutorial on how to build a full stack To-Do application using React Javascript Framework and Django Web Framework.

React is a framework that is used for building Single Page Applications(SPA's).React was introduced by Facebook now Meta and has a wonderful community of developers and a slick [documentation](https://react.dev/).

Django on the other hand is a simple web framework  that aims to simplify web development.Django framework have a lot of ready made libraries and a  vibrant [documentation] (https://docs.djangoproject.com/en/4.2/) that supports developer needs.


In this application, React Framework serves as the  client-side that handles the UI(User Interface). It also gets and sends data via requests to the Django Framework backend with an API build atop the Django REST Framework(DRF).

This is a demo of the application:
This web application allows users to create tasks,   mark the tasks as **complete** or **incomplete**.

- **PREREQUISTES**
- **BACKEND**
- **API**
- **SERIALIZERS**
- **FRONTEND**

**PREREQUISITES**
In this tutorial, you must have:

- Set up a local programming environment for [python3](https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-ubuntu-20-04)

- Set up [Node.js](https://www.digitalocean.com/community/tutorial_series/how-to-install-node-js-and-create-a-local-development-environment) on your Local Development Environment.

**SETTING UP DJANGO BACKEND**
This project was built entirely on a Linux machine, however, with slight modifications,the commands are just about the same on other Operating Systems(0S).

Under this section, a new project directory will be created where Django will be installed.
Open a new terminal and create a project directory:

`mkdir react-django-todo`

Navigate into the directory by issuing the {cd} (change directory) command on the terminal.

`cd react-django-todo`

Install Pipenv using pip in order to create a virtual environment.
Depending on your OS, you may need to use `pip3 instead of pip`

`pip install pipenv`

Now activate the virtual environment by running the command:
`pipenv shell`

Install Django in the virtual environment by running the command:
 
`pipenv install django`

This allows us to use any version of Django, Python Programming Language and all other dependencies in a closed environment/sandboxed without breaking anything on our system.

Next, we create a new project directory **backend** with either of the commands:

`django-admin startproject backend` or
`python3 manage.py startproject backend`

Note: Depending on your installation or OS, you may need to use **python3** or **python**.

Navigate to the newly created directory:

`cd backend`

Create and start a new application in the same directory:

`python3 manage.py startapp todo`

In the next steps, perform migrations:

`python3 manage.py migrate`

Start up the server with the command:

`python3 manage.py runserver`

Press and hold the **Ctrl Key** and click on the  link **http://localhost:8000** to open it in your browser.

![Django Installation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fox57iz0ke9i1gdf54o7.png)

**REGISTERING THE todo APPLICATION IN DJANGO**
Django applications are modular as pieces of functionality are built into them. The setup for the backend is now complete and all applications in Django are registered as installed apps so Django can recognize them.
INSTALLED_APPS can be found in the directory 'backend/setting.py'.


![todo app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgdugksxc0exl9y7tk6q.png)

Django comes with SQLite database which is minimal and great for side projects.

**CREATING A todo MODEL**
In this section, we will create a model that describes how todo items should be saved in the database.

Navigate to the directory 'todo/models.py' and add the lines of code:

```
from django.db import models

# Create your models here.

class Todo(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField()
    completed = models.BooleanField(default=False)

    def _str_(self):
        return self.title
```
This code describes three properties of the todo model.

1. **title** of the task

2. **description** of the task

3.status of the task, **completed or not completed** 

Once a model has been created, we need to migrate the model into the database:

`python3 manage.py makemigrations todo
`

In the next step, the changes will be applied to the database:
`python3 manage.py migrate`

You may now test the Create, Read, Update and Delete (CRUD) operations on the Todo application by using the admin interface provided by Django by default.

Open todo/admin.py and the following code:

```
from django.contrib import admin
from .models import Todo

class TodoAdmin(admin.ModelAdmin):
    list_display = ('title', 'description', 'completed')

(Register your models here).

admin.site.register(Todo, TodoAdmin)

```


The next step is to create a "superuser" account to access the admin interface. Run the command in the terminal:

`python3 manage.py createsuperuser`

This command sends a prompt that requests for 'username', 'email address', and 'password'.Enter a username and password you will remember because you will use it to log into the admin dashboard interface.

Start the server again with the command:

`python3 manage.py runserver`

Navigate to the link http://localhost:8080/admin in your browser to access the admin interface and log in with the username and password you created earlier.

![Admin Django Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/891iekhe2miy3akhxlf3.png)

It is now possible to CRUD the **Todo items** from this dashboard.

You can now add items to the admin dashboard.

![ToDo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1moc4khbyq5xskgedtd.png)

**SETTING UP APIs**

In this part, Django REST framework will be used to create the API(Application Programming Interface).
While still in the virtual environment, install the djangorestframework and django cross origin resource sharing headers with the commands:

`
pipenv install djangorestframework django-cors-headers
`
Next we need to add the djangorestframework and django-cors-headers to the list of installed apps in the todo/settings.py file.

![django cors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkomanova77i0sxurd7d.png)

Still in the todo/settings.py file, add the following lines at the bottom of the file:

```
CORS_ORIGIN_WHITELIST = [
     'http://localhost:3000'
]
```
Django CORS is mechanism that allows clients to consume APIs that are hosted on a different domain. In other words, it is a library in Python that ensures that specific sets of headers that are required by servers are served by browsers.
The CORS_ORIGIN_WHITELIST code means that the domain 'localhost:3000' has been whitelisted and the browser will allow Cross Origin Resources to be shared on that domain.
The frontend of the application will be served on that port hence whitelisting it so the frontend can communicate with the backend.

**CREATING SERIALIZERS**
Serializers in Python-Django convert model instances to JSON(Javascript Object Notation) enabling the frontend to work with the received data from the backend.

Create a todo/serializers.py file and update it with the following lines:

```
from rest_framework import serializers
from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'description', 'completed')
```
The model data will be converted to JSON format.

**CREATING View**
In this section, we wil create a TodoView class in the todo/views.py file. Add the following lines to the todo/views.py:

```
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo

class TodoView(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()
```
**Viewsets** class in Django provides implementation for CRUD operations by default. In the above code, there is a serializer class and a queryset.

We need to update the backend/urls.py file with the view we just created. Open the backend/urls.py file and add the following lines: 

```
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from todo import views

router = routers.DefaultRouter()
router.register(r'todos', views.TodoView, 'todo')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
]
```
URL path for the API has been indicated in the above code. API has finally been created and this section has now been completed.

CRUD operations can now be performed on the Todo model. 
The router class allows us to make the following queries:
/todos/ -  returns a list of Todo items. CREATING of an item,  an item can be performed here
/todos/id - returns a single item using the 'id' primary key where UPDATING and DELETION are allowed.

Restart the server with the command;
`python3 manage.py runserver
`



![todo app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofr32wc2os9nutxhqj01.png)

**SETTING UP THE FRONTEND**
In this section, we will complete the web application by writing the frontend.

Open another terminal and navigate to the directory 'react-django-todo'
In this tutorial, I will use npx to Create-React-App:

`npx create-react-app frontend`

`cd frontend`

Start the application by running the command:

`npm start`

The browser will open the http://localhost:3000.
In the next step, we will install bootstrap and reactstrap

`npm install boostrap reactstrap --legacy-peer-deps`

In a scenario where your system encounters a dependency issue based either a incompatible ReactJS, Bootstrap or Reactstrap package, visit this [link](https://github.com/reactstrap/reactstrap/issues/2045) for further guidelines.

Open frontend/index.js and paste these lines:

```
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```






Open frontend/App.js and paste these lines:

```
import React, { Component } from "react";

const todoItems = [
  {
    id: 1,
    title: "Go to Market",
    description: "Buy ingredients to prepare dinner",
    completed: true,
  },
  {
    id: 2,
    title: "Study",
    description: "Read Algebra and History textbook for the upcoming test",
    completed: false,
  },
  {
    id: 3,
    title: "Sammy's books",
    description: "Go to library to return Sammy's books",
    completed: true,
  },
  {
    id: 4,
    title: "Article",
    description: "Write article on how to use Django with React",
    completed: false,
  },
];

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewCompleted: false,
      todoList: todoItems,
    };
  }

  displayCompleted = (status) => {
    if (status) {
      return this.setState({ viewCompleted: true });
    }

    return this.setState({ viewCompleted: false });
  };

  renderTabList = () => {
    return (
      <div className="nav nav-tabs">
        <span
          className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
          onClick={() => this.displayCompleted(true)}
        >
          Complete
        </span>
        <span
          className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
          onClick={() => this.displayCompleted(false)}
        >
          Incomplete
        </span>
      </div>
    );
  };

  renderItems = () => {
    const { viewCompleted } = this.state;
    const newItems = this.state.todoList.filter(
      (item) => item.completed == viewCompleted
    );

    return newItems.map((item) => (
      <li
        key={item.id}
        className="list-group-item d-flex justify-content-between align-items-center"
      >
        <span
          className={`todo-title mr-2 ${
            this.state.viewCompleted ? "completed-todo" : ""
          }`}
          title={item.description}
        >
          {item.title}
        </span>
        <span>
          <button
            className="btn btn-secondary mr-2"
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
          >
            Delete
          </button>
        </span>
      </li>
    ));
  };

  render() {
    return (
      <main className="container">
        <h1 className="text-white text-uppercase text-center my-4">Todo app</h1>
        <div className="row">
          <div className="col-md-6 col-sm-10 mx-auto p-0">
            <div className="card p-3">
              <div className="mb-4">
                <button
                  className="btn btn-primary"
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
      </main>
    );
  }
}

export default App;
```



















 This code is although not dynamic as some values have been hardcoded, but then they are only temporary as once the data is fetched from the backend they are replaced.

The renderTablist() function helps span what is displayed. Clicking the **Completed Tab** will display completed tasks and clicking the **Incomplete Tab** will display the yet to be completed tasks.

Save changes and from the two terminals, serve the frontend and backend at the same time.


![Full App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzuiv675wuo95pc3ilkp.png)

[Source code is available for free here github](https://github.com/Kalderon-Sheikhman/Todo-Full-Stack-App)