Tutorial

How To Build a To-Do application Using Django and React

Updated on February 18, 2021
author

Jordan Irabor

How To Build a To-Do application Using Django and React

Introduction

In this tutorial, you will build a To-Do application using Django and React.

React is a JavaScript framework for developing SPAs (single-page applications). It has solid documentation and a vibrant ecosystem around it.

Django is a Python web framework that simplifies common practices in web development. Django is reliable and also has a vibrant ecosystem of stable libraries supporting common development needs.

For this application, React serves as the frontend, or client-side framework, handling the user interface and getting and setting data via requests to the Django backend, which is an API built using the Django REST framework (DRF).

At the end of this tutorial, you will have a fully working application:

Animated gif of a user interacting with the application to create a new task and toggle it between complete and incomplete statuses.

If you want to deploy the app in this tutorial, you can deploy directly from a GitHub repo using DigitalOcean App Platform.

This application will allow users to create tasks and mark them as complete or incomplete.

Prerequisites

To follow along with this tutorial, you will need to:

  1. Install and set up a local programming environment for Python 3
  2. Install Node.js and Create a Local Development Environment

This tutorial was verified with Python v3.9.1, pip v20.2.4, Django v3.1.6, djangorestframework v3.12.2, django-cors-headers v3.7.0, Node v15.8.0, npm v7.5.4, React v17.0.1, and axios v0.21.0.

Step 1 — Setting Up the Backend

In this section, you will create a new project directory and install Django.

Open a new terminal window and run the following command to create a new project directory:

  1. mkdir django-todo-react

Next, navigate into the directory:

  1. cd django-todo-react

Now install Pipenv using pip:

  1. pip install pipenv

Note: Depending on your installation, you may need to use pip3 instead of pip.

And activate a new virtual environment:

  1. pipenv shell

Install Django using Pipenv:

  1. pipenv install django

Then create a new project called backend:

  1. django-admin startproject backend

Next, navigate into the newly created backend directory:

  1. cd backend

Start a new application called todo:

python manage.py startapp todo

Run migrations:

python manage.py migrate

And start up the server:

python manage.py runserver

Navigate to http://localhost:8000 in your web browser:

Screenshot of the default Django application running successfully.

At this point, you will see an instance of a Django application running successfully. Once you are finished, you can stop the server (CONTROL+C or CTRL+C).

Registering the todo Application

Now that you have completed the setup for the backend, you can begin registering the todo application as an installed app so that Django can recognize it.

Open the backend/settings.py file in your code editor and add todo to the INSTALLED_APPS:

backend/settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo',
]

Then, save your changes.

Defining the Todo Model

Let’s create a model to define how the Todo items should be stored in the database.

Open the todo/models.py file in your code editor and add the following lines of code:

todo/models.py
from django.db import models

# Create your models here.

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

    def _str_(self):
        return self.title

The code snippet above describes three properties on the Todo model:

  • title
  • description
  • completed

The completed property is the status of a task. A task will either be completed or not completed at any time. Because you have created a Todo model, you will need to create a migration file:

  1. python manage.py makemigrations todo

And apply the changes to the database:

  1. python manage.py migrate todo

You can test to see that CRUD operations work on the Todo model you created by using the admin interface that Django provides by default.

Open the todo/admin.py file with your code editor and add the following lines of code:

todo/admin.py
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)

Then, save your changes.

You will need to create a “superuser” account to access the admin interface. Run the following command in your terminal:

  1. python manage.py createsuperuser

You will be prompted to enter a username, email, and password for the superuser. Be sure to enter details that you can remember because you will need them to log in to the admin dashboard.

Start the server once again:

  1. python manage.py runserver

Navigate to http://localhost:8000/admin in your web browser. And log in with the username and password that was created earlier:

Screenshot of the admin interface for the Django application.

You can create, edit, and, delete Todo items using this interface:

Screenshot of the admin interface for the Django application displaying todo items.

After experimenting with this interface, you can stop the server (CONTROL+C or CTRL+C).

Step 2 — Setting Up the APIs

In this section, you will create an API using the Django REST framework.

Install the djangorestframework and django-cors-headers using Pipenv:

  1. pipenv install djangorestframework django-cors-headers

You need to add rest_framework and corsheaders to the list of installed applications. Open the backend/settings.py file in your code editor and update the INSTALLED_APPS and MIDDLEWARE sections:

backend/settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'rest_framework',
    'todo',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

Then, add these lines of code to the bottom of the backend/settings.py file:

backend/settings.py
CORS_ORIGIN_WHITELIST = [
     'http://localhost:3000'
]

django-cors-headers is a Python library that will prevent the errors that you would normally get due to CORS rules. In the CORS_ORIGIN_WHITELIST code, you whitelisted localhost:3000 because you want the frontend (which will be served on that port) of the application to interact with the API.

Creating serializers

You will need serializers to convert model instances to JSON so that the frontend can work with the received data.

Create a todo/serializers.py file with your code editor. Open the serializers.py file and update it with the following lines of code:

todo/serializers.py
from rest_framework import serializers
from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'description', 'completed')

This code specifies the model to work with and the fields to be converted to JSON.

Creating the View

You will need to create a TodoView class in the todo/views.py file.

Open the todo/views.py file with your code editor and add the following lines of code:

todo/views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo

# Create your views here.

class TodoView(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()

The viewsets base class provides the implementation for CRUD operations by default. This code specifies the serializer_class and the queryset.

Open the backend/urls.py file with your code editor and replace the contents with the following lines of code:

backend/urls.py
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)),
]

This code specifies the URL path for the API. This was the final step that completes the building of the API.

You can now perform CRUD operations on the Todo model. The router class allows you to make the following queries:

  • /todos/ - returns a list of all the Todo items. CREATE and READ operations can be performed here.
  • /todos/id - returns a single Todo item using the id primary key. UPDATE and DELETE operations can be performed here.

Let’s restart the server:

  1. python manage.py runserver

Navigate to http://localhost:8000/api/todos in your web browser:

Screenshot of the API results for the Todo items.

You can CREATE a new Todo item using the interface:

Screenshot of the API tool for creating new Todo items.

If the Todo item is created successfully, you will be presented with a successful response:

Screenshot of the API response for a successful Todo item creation.

You can also perform DELETE and UPDATE operations on specific Todo items using the id primary keys. Use the address structure /api/todos/{id} and provide an id.

Add 1 to the URL to examine the Todo item with the id of “1”. Navigate to http://localhost:8000/api/todos/1 in your web browser:

Screenshot of API tools for DELETE and PUT.

This completes the building of the backend of the application.

Step 3 — Setting Up the Frontend

Now that you have the backend of the application complete, you can create the frontend and have it communicate with the backend over the interface that you created.

First, open a new terminal window and navigate to the django-todo-react project directory.

To set up the frontend, this tutorial will rely upon Create React App. There are several approaches to using create-react-app. One approach is to use npx to run the package and create the project:

  1. npx create-react-app frontend

You can learn more about this approach by reading the How To Set Up a React Project with Create React App.

After the project is created, you can change into the newly created frontend directory:

  1. cd frontend

Then, start the application:

  1. npm start

Your web browser will open http://localhost:3000 and you will be presented with the default Create React App screen:

Screenshot of landing page for the default Create React App application.

Next, install bootstrap and reactstrap to provide user interface tools.

  1. npm install bootstrap@4.6.0 reactstrap@8.9.0 --legacy-peer-deps

Note: You may encounter unable to resolve dependency tree errors depending on your versions of React, Bootstrap, and Reactstrap.

At the time of the revision, the latest version of popper.js has been deprecated and will conflict with React 17+. This is a known issue and it is possible to use the --legacy-peer-deps option when installing.

Open index.js in your code editor and add bootstrap.min.css:

frontend/src/index.js
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();

If you are having difficulty with this step, you can consult the official documentation for adding bootstrap.

Open App.js in your code editor and add the following lines of code:

frontend/src/App.js
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 includes some hardcoded values for four items. These will be temporary values until items are fetched from the backend.

The renderTabList() function renders two spans that help control which set of items are displayed. Clicking on the Completed tab will display the completed tasks. Clicking on the Incomplete tab will display the incomplete tasks.

Save your changes and observe the application in your web browser:

Screenshot of the application currently displaying tasks for Study and Article.

To handle actions such as adding and editing tasks, you will need to create a modal component.

First, create a components folder in the src directory:

  1. mkdir src/components

Then, create a Modal.js file and open it with your code editor. Add the following lines of code:

frontend/src/components/Modal.js
import React, { Component } from "react";
import {
  Button,
  Modal,
  ModalHeader,
  ModalBody,
  ModalFooter,
  Form,
  FormGroup,
  Input,
  Label,
} from "reactstrap";

export default class CustomModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeItem: this.props.activeItem,
    };
  }

  handleChange = (e) => {
    let { name, value } = e.target;

    if (e.target.type === "checkbox") {
      value = e.target.checked;
    }

    const activeItem = { ...this.state.activeItem, [name]: value };

    this.setState({ activeItem });
  };

  render() {
    const { toggle, onSave } = this.props;

    return (
      <Modal isOpen={true} toggle={toggle}>
        <ModalHeader toggle={toggle}>Todo Item</ModalHeader>
        <ModalBody>
          <Form>
            <FormGroup>
              <Label for="todo-title">Title</Label>
              <Input
                type="text"
                id="todo-title"
                name="title"
                value={this.state.activeItem.title}
                onChange={this.handleChange}
                placeholder="Enter Todo Title"
              />
            </FormGroup>
            <FormGroup>
              <Label for="todo-description">Description</Label>
              <Input
                type="text"
                id="todo-description"
                name="description"
                value={this.state.activeItem.description}
                onChange={this.handleChange}
                placeholder="Enter Todo description"
              />
            </FormGroup>
            <FormGroup check>
              <Label check>
                <Input
                  type="checkbox"
                  name="completed"
                  checked={this.state.activeItem.completed}
                  onChange={this.handleChange}
                />
                Completed
              </Label>
            </FormGroup>
          </Form>
        </ModalBody>
        <ModalFooter>
          <Button
            color="success"
            onClick={() => onSave(this.state.activeItem)}
          >
            Save
          </Button>
        </ModalFooter>
      </Modal>
    );
  }
}

This code creates a CustomModal class and it nests the Modal component that is derived from the reactstrap library.

This code also defined three fields in the form:

  • title
  • description
  • completed

These are the same fields that we defined as properties on the Todo model in the backend.

The CustomModal receives activeItem, toggle, and onSave as props:

  1. activeItem represents the Todo item to be edited.
  2. toggle is a function used to control the Modal’s state (i.e., open or close the modal).
  3. onSave is a function that is called to save the edited values of the Todo item.

Next, you will import the CustomModal component into the App.js file.

Revisit the src/App.js file with your code editor and replace the entire contents with the following lines of code:

frontend/src/App.js
import React, { Component } from "react";
import Modal from "./components/Modal";

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,
      modal: false,
      activeItem: {
        title: "",
        description: "",
        completed: false,
      },
    };
  }

  toggle = () => {
    this.setState({ modal: !this.state.modal });
  };

  handleSubmit = (item) => {
    this.toggle();

    alert("save" + JSON.stringify(item));
  };

  handleDelete = (item) => {
    alert("delete" + JSON.stringify(item));
  };

  createItem = () => {
    const item = { title: "", description: "", completed: false };

    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  editItem = (item) => {
    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  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"
            onClick={() => this.editItem(item)}
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
            onClick={() => this.handleDelete(item)}
          >
            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"
                  onClick={this.createItem}
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
        {this.state.modal ? (
          <Modal
            activeItem={this.state.activeItem}
            toggle={this.toggle}
            onSave={this.handleSubmit}
          />
        ) : null}
      </main>
    );
  }
}

export default App;

Save your changes and observe the application in your web browser:

Screenshot of the Modal component displaying a new task with a title of - Study - and a description of - Read Algebra and History textbook for the upcoming test

If you attempt to edit and save a Todo item, you will get an alert displaying the Todo item’s object. Clicking on Save or Delete will perform the respective actions on the Todo item.

Note:: Depending on your version of React and Reactstrap, you may experience console errors. At the time of the revision, Warning: Legacy context API has been detected within a strict-mode tree. and Warning: findDOMNode is deprecated in StrictMode. are known issues.

Now, you will modify the application so that it interacts with the Django API you built in the previous section. Revisit the first terminal window and ensure the server is running. If it is not running, use the following command:

  1. python manage.py runserver

Note: If you closed this terminal window, remember that you will need to navigate to the backend directory and use the virtual Pipenv shell.

To make requests to the API endpoints on the backend server, you will install a JavaScript library called axios.

In the second terminal window, ensure that you are in the frontend directory and install axios:

  1. npm install axios@0.21.1

Then open the frontend/package.json file in your code editor and add a proxy:

frontend/package.json
[...]
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:8000",
  "dependencies": {
    "axios": "^0.18.0",
    "bootstrap": "^4.1.3",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-scripts": "2.0.5",
    "reactstrap": "^6.5.0"
  },
[...]

The proxy will help in tunneling API requests to http://localhost:8000 where the Django application will handle them. Without this proxy, you would need to specify full paths:

axios.get("http://localhost:8000/api/todos/")

With proxy, you can provide relative paths:

axios.get("/api/todos/")

Note: You might need to restart the development server for the proxy to register with the application.

Revisit the frontend/src/App.js file and open it with your code editor. In this step, you will remove the hardcoded todoItems and use data from requests to the backend server. handleSubmit and handleDelete

Open the App.js file and replace it with this final version:

frontend/src/App.js
import React, { Component } from "react";
import Modal from "./components/Modal";
import axios from "axios";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewCompleted: false,
      todoList: [],
      modal: false,
      activeItem: {
        title: "",
        description: "",
        completed: false,
      },
    };
  }

  componentDidMount() {
    this.refreshList();
  }

  refreshList = () => {
    axios
      .get("/api/todos/")
      .then((res) => this.setState({ todoList: res.data }))
      .catch((err) => console.log(err));
  };

  toggle = () => {
    this.setState({ modal: !this.state.modal });
  };

  handleSubmit = (item) => {
    this.toggle();

    if (item.id) {
      axios
        .put(`/api/todos/${item.id}/`, item)
        .then((res) => this.refreshList());
      return;
    }
    axios
      .post("/api/todos/", item)
      .then((res) => this.refreshList());
  };

  handleDelete = (item) => {
    axios
      .delete(`/api/todos/${item.id}/`)
      .then((res) => this.refreshList());
  };

  createItem = () => {
    const item = { title: "", description: "", completed: false };

    this.setState({ activeItem: item, modal: !this.state.modal });
  };

  editItem = (item) => {
    this.setState({ activeItem: item, modal: !this.state.modal });
  };

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

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

  renderTabList = () => {
    return (
      <div className="nav nav-tabs">
        <span
          onClick={() => this.displayCompleted(true)}
          className={this.state.viewCompleted ? "nav-link active" : "nav-link"}
        >
          Complete
        </span>
        <span
          onClick={() => this.displayCompleted(false)}
          className={this.state.viewCompleted ? "nav-link" : "nav-link active"}
        >
          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"
            onClick={() => this.editItem(item)}
          >
            Edit
          </button>
          <button
            className="btn btn-danger"
            onClick={() => this.handleDelete(item)}
          >
            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"
                  onClick={this.createItem}
                >
                  Add task
                </button>
              </div>
              {this.renderTabList()}
              <ul className="list-group list-group-flush border-top-0">
                {this.renderItems()}
              </ul>
            </div>
          </div>
        </div>
        {this.state.modal ? (
          <Modal
            activeItem={this.state.activeItem}
            toggle={this.toggle}
            onSave={this.handleSubmit}
          />
        ) : null}
      </main>
    );
  }
}

export default App;

The refreshList() function is reusable that is called each time an API request is completed. It updates the Todo list to display the most recent list of added items.

The handleSubmit() function takes care of both the create and update operations. If the item passed as the parameter doesn’t have an id, then it has probably not been created, so the function creates it.

At this point, verify that your backend server is running in your first terminal window:

  1. python manage.py runserver

Note: If you closed this terminal window, remember that you will need to navigate to the backend directory and use the virtual Pipenv shell.

And in your second terminal window, ensure that you are in the frontend directory and start your frontend application:

  1. npm start

Now when you visit http://localhost:3000 with your web browser, your application will allow you to READ, CREATE, UPDATE, and DELETE tasks.

Animated gif of a user interacting with the application to create a new item and toggle it between complete and incomplete statuses.

This completes the frontend and backend of the Todo application.

Conclusion

In this article, you built a To-Do application using Django and React. You achieved this with the djangorestframework, django-cors-headers, axios, bootstrap, and reactstrap libraries.

If you’d like to learn more about Django, check out our Django topic page for exercises and programming projects.

If you’d like to learn more about React, take a look at our How To Code in React.js series, or check out our React topic page for exercises and programming projects.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author(s)

Category:
Tutorial

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
43 Comments
Leave a comment...

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Ran into this error,

“?: (corsheaders.E013) Origin ‘localhost:3000/’ in CORS_ORIGIN_WHITELIST is missing scheme or netloc HINT: Add a scheme (e.g. https://) or netloc (e.g. example.com).”

The fix for this is to remove the trailing ‘/’ and prefix the "http://’ before local host. After making this change in the backend\settings.py file in the ‘CORS_ORIGIN_WHITELIST’ section, this worked for me.

Also, you need a ‘,’ at the end of the item “http://localhost:8000” in the CORS_ORIGIN_WHITELIST list

CORS_ORIGIN_WHITELIST = (
    'https://localhost:3000',
)

How to deploy this to DigitalOcean?

Use docker to run the frontend and the backend and define the network for them. You can also run the database with docker. Then just configure an webserver on droplet and run these containers.

can you tell how to deploy the same on azure

Great tutorial.

I have a couple of questions being a total noob.

What is the difference between the method definitions and the arrow functions in a class in javascript? They both appear to act as method functions, is there a scope difference or something?

And for the Input and Label tags in Modal.js, you seemed to have mapped the “for” attribute in Label and the “name” attribute in Input. Did you deliberately omit the “id” attribute because you didn’t want the effect?

great article, thanks a lot !! If you ran into the error “?: (corsheaders.E013) Origin ‘localhost:3000/’ in CORSORIGINWHITELIST is missing scheme or netloc HINT: Add a scheme (e.g. https://) or netloc (e.g. example.com).” Same as bellow try this

CORS_ORIGIN_WHITELIST = [
     'https://localhost:3000'
]

Today I learn that django care about the brackets !

Same for me, the option wouldn’t read if parenthesis was used instead of square brackets

Yeah, it’s a subtle thing. To make a tuple with a single item, you need a comma, so (‘https://localhost:3000’,) works. But (‘https://localhost:3000’) gets treated as just the string ‘https://localhost:3000’. Lists don’t have this issue because square brackets aren’t used for marking an expression.

  1. You added localhost:8000 as a proxy and you mentioned the purpose of it. However, you still left it in your axios requests

  2. I believe you’ll be getting forbidden requests unless you include CSRF tokens as well.

  3. you already used npm to create the react app. Why are you suddenly using yarn? The tutorial gets very confusing when it’s just blindly copy/pasting code.

After registering todo app then run command::python manage.py createsuperuser Got errors- <class ‘todo.admin.TodoAdmin’>: (admin.E108) The value of ‘list_display[2]’ refers to ‘completed’, which is not a callable, an attribute of ‘TodoAdmin’, or an attribute or method on ‘todo.Todo’.

Please help me to resolve this

Hi, thank you for this tutorial.

I completed the exercise but I am getting “Forbidden” messages when I try to DELETE. How can I fix this part?

Also, what can I do to refresh the GUI after any operation. For example, I would like if after checking a TODO task “complete”, it will immediately disappear from the list of incomplete tasks.

The code is missing slash at the end of delete url:

handleDelete = item => {
axios
  .delete(`http://localhost:8000/api/todos/${item.id}/`)  // add it at here

How to deploy these 2 app at once

I managed to get past the CORSORIGINWHITELIST error and a couple of others, but from the point at which I edit the src/App.js file I am stuck with the default React screen no matter what I do. Does anyone know what I might have missed? Environment is an Ubuntu PC, everything on the BASH terminal and using Chromium and Firefox as browsers.

UPDATE: Deleted and rebuilt the entire project from scratch a couple of times and the error remains entirely consistent. I managed to suppress it by deleting the two lines referring to serviceWorker from index.js. Needless to say this is most unsatisfying and will return to bite me on the gluteus maximus. (As soon as he gets back from the set of “Gladiator”.) Any comments on the real solution would be very helpful!

In the index.js, just add import 'bootstrap/dist/css/bootstrap.min.css'; line, don’t change other lines in the original file.

Had to import bootstrap explicitly like so:

import '../node_modules/bootstrap/dist/css/bootstrap.min.css';

otherwise bootstrap wouldn’t load.

Good tutorial but you didn’t show us how to deploy it?

GET is working just fine, but POST, PUT, and DELETE are returning 403 errors

I’m getting this error,

./src/index.js Module not found: Can’t resolve ‘./serviceWorker’ in ‘/django-todo-react/frontend/src’

Thanks for the tutorial. Everything works great, though for future readers I would point the 2 problems I most had trouble with

  1. “?: (corsheaders.E013) Origin ‘localhost:3000/’ in CORSORIGINWHITELIST As others have mentioned I encountered this error early on the solution for me was:
CORS_ORIGIN_WHITELIST = [
     'https://localhost:3000'
]
  1. The proxy for the axios in the frontend is supposed to change
axios.get("http://localhost:8000/api/todos/")

to

axios.get("/api/todos/")

However, the final App.js file is still using the former one which caused my list to not show up. You need to edit and change them to the latter in App.js

I agree, this was the only versioning issue that I came across as well.

“”“Add a scheme (e.g. https://) or netloc (e.g. example.com).”“” “”“https://github.com/adamchainz/django-cors-headers/issues/403"”"

CORS_ORIGIN_WHITELIST = [‘http://localhost:3000’]

Great work on this tutorial. This is one of the best examples I’ve seen.

What are the pros and cons of using Django Templates for the front-end in place of React?

Thanks for the great tutorial. For those still struggling with the POST,PUT,DELETE or related requests, it’s most likely because your requests aren’t authenticated. A quick fix would be to first create a crsftoken then include it in the requests. Here’s a related code snippet for the fix.

Thanks for your suggestion. I am also getting for example when creating a new task

Forbidden: /api/todos/
[15/Mar/2021 11:16:56] "POST /api/todos/ HTTP/1.1" 403 58

I guess your approach could solve the problem but since this is the first thing ever done in js could you maybe add details on where exactly to put your code snippets so that the application will work?

There’s an easier solution, at least it doesn’t require modification of the request bodies. The CSRF token is only needed because Django REST Framework, by default, has session authentication enabled. If you remove that from the set of authentication classes, it will work without any change on the frontend. For a demo app, that should be fine.

Add the following to your settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
    ]
}

This applies if you see the following error in your response body after submitting PUT/POST/DELETE request:

{"detail":"CSRF Failed: CSRF token missing or incorrect."}

it solves for me the forbidden response !!! thanks so much !!!

I was having the same issue, Thanks for the fix!

Also thanks for the great tutorial! I found the second answer here helpful. On a mac, using chrome, I set the defaults below the axios imports as shown below and I was able to post, put and delete.

    import axios from 'axios';
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";

andrewd’s solution worked for me. The 403 forbidden was the only problem I encountered. Thanks for a great tutorial!

I was able get Data from backend but unable to post, put, delete it. And your solution worked for me, Thanks. I am working on Windows 10 btw.

Couple of issues I ran into: 2 total 1 of 2. In the frontend/src/index.js file regarding import * as serviceWorker from ‘./serviceWorker’;

Issue: kept getting: ./src/index.jsModule not found: Can’t resolve ‘./serviceWorker’ in …

Solution: From https://stackoverflow.com/questions/65255315/module-not-found-cant-resolve-serviceworker-in-myapp-src ( user: Maxime Bodivit )

==== START of file: serivceWorker.js ====
// This optional code is used to register a service worker.
// register() is not called by default.

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.

// To learn more about the benefits of this model and instructions on how to

const isLocalhost = Boolean(
    window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.0/8 are considered localhost for IPv4.
    window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export function register(config) {
    if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
        // The URL constructor is available in all browsers that support SW.
        const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
        if (publicUrl.origin !== window.location.origin) {
            // Our service worker won't work if PUBLIC_URL is on a different origin
            // from what our page is served on. This might happen if a CDN is used to
            // serve assets; see https://github.com/facebook/create-react-app/issues/2374
            return;
        }

        window.addEventListener('load', () => {
            const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

            if (isLocalhost) {
                // This is running on localhost. Let's check if a service worker still exists or not.
                checkValidServiceWorker(swUrl, config);

                // Add some additional logging to localhost, pointing developers to the
                // service worker/PWA documentation.
                navigator.serviceWorker.ready.then(() => {
                    console.log(
                        'This web app is being served cache-first by a service '
                    );
                });
            } else {
                // Is not localhost. Just register service worker
                registerValidSW(swUrl, config);
            }
        });
    }
}

function registerValidSW(swUrl, config) {
    navigator.serviceWorker
        .register(swUrl)
        .then(registration => {
            registration.onupdatefound = () => {
                const installingWorker = registration.installing;
                if (installingWorker == null) {
                    return;
                }
                installingWorker.onstatechange = () => {
                    if (installingWorker.state === 'installed') {
                        if (navigator.serviceWorker.controller) {
                            // At this point, the updated precached content has been fetched,
                            // but the previous service worker will still serve the older
                            // content until all client tabs are closed.
                            console.log(
                                'New content is available and will be used when all '
                            );

                            // Execute callback
                            if (config && config.onUpdate) {
                                config.onUpdate(registration);
                            }
                        } else {
                            // At this point, everything has been precached.
                            // It's the perfect time to display a
                            // "Content is cached for offline use." message.
                            console.log('Content is cached for offline use.');

                            // Execute callback
                            if (config && config.onSuccess) {
                                config.onSuccess(registration);
                            }
                        }
                    }
                };
            };
        })
        .catch(error => {
            console.error('Error during service worker registration:', error);
        });
}

function checkValidServiceWorker(swUrl, config) {
    // Check if the service worker can be found. If it can't reload the page.
    fetch(swUrl, {
            headers: {
                'Service-Worker': 'script'
            },
        })
        .then(response => {
            // Ensure service worker exists, and that we really are getting a JS file.
            const contentType = response.headers.get('content-type');
            if (
                response.status === 404 ||
                (contentType != null && contentType.indexOf('javascript') === -1)
            ) {
                // No service worker found. Probably a different app. Reload the page.
                navigator.serviceWorker.ready.then(registration => {
                    registration.unregister().then(() => {
                        window.location.reload();
                    });
                });
            } else {
                // Service worker found. Proceed as normal.
                registerValidSW(swUrl, config);
            }
        })
        .catch(() => {
            console.log(
                'No internet connection found. App is running in offline mode.'
            );
        });
}

export function unregister() {
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.ready
            .then(registration => {
                registration.unregister();
            })
            .catch(error => {
                console.error(error.message);
            });
    }

==== END of file: serviceWorker.js ====

2 of 2: The CORS_ORIGIN_WHITELIST url issue:

# Added this to the backend/settings.py file 
CORS_ORIGIN_WHITELIST = (
     'http://localhost:3000', #it needs the comma to ensure this is a tuple
 )

This comment has been deleted

    Great concise Tutorial, no need to force people to build Rome when the need is a small implementation.

    This is awesome. I’ve never seen anyone add React to Django this way and I’m wondering the difficulty of deployment with this set up?

    I haven’t even got that far and I have run into this error, can anyone help?

    Just after i completed step 2 i got this when going on localhost:8000/todos

    Error: OperationalError at /api/todos/ no such table: todo_todo

    Except at Initial steps regarding creating a virtual environment. This is a perfect post for beginners and everything working fine

    My name is Alauddin. I am learning to react and Django together. I got a valuable article for myself. I am trying to do this. If I get any error I will surely ask you. Thank you for sharing.

    POST http://localhost:8000/api/todos/ 400 (Bad Request)

    got this error, probably some problem in my Django app, any help would be appreciated, thanks in advance :)

    Bobby Iliev
    Site Moderator
    Site Moderator badge
    August 4, 2021

    Hi there,

    In your ALLOWED_HOSTS make sure to specify the following:

    ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'your_domain']
    

    Hi @bobbyiliev actually I tried putting the hosts you mentioned and I also tried doing ALLOWED_HOSTS = [‘*’], but no success same error still…

    Bobby Iliev
    Site Moderator
    Site Moderator badge
    August 8, 2021

    Hi there @metkar9699130773,

    In this case, I could suggest enabling Debug mode to see if you could get some more information about the problem rather than the 400 error.

    Let me know how it goes!

    After setting up back-end and creating api ! when i restart server. I get this

    System check identified no issues (0 silenced).
    August 09, 2021 - 07:22:02
    Django version 3.2.6, using settings 'backend.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    Exception in thread django-main-thread:
    Traceback (most recent call last):
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\servers\basehttp.py", line 45, in get_internal_wsgi_application
        return import_string(app_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\module_loading.py", line 17, in import_string
        module = import_module(module_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\importlib\__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 855, in exec_module
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File "D:\Coding\React\django-todo-react\backend\backend\wsgi.py", line 16, in <module>
        application = get_wsgi_application()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\wsgi.py", line 13, in get_wsgi_application
        return WSGIHandler()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\handlers\wsgi.py", line 127, in __init__
        self.load_middleware()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\handlers\base.py", line 40, in load_middleware
        middleware = import_string(middleware_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\module_loading.py", line 17, in import_string
        module = import_module(module_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\importlib\__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
    ModuleNotFoundError: No module named 'corshaders'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\threading.py", line 954, in _bootstrap_inner
        self.run()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\threading.py", line 892, in run
        self._target(*self._args, **self._kwargs)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
        fn(*args, **kwargs)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 138, in inner_run
        handler = self.get_handler(*args, **options)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\contrib\staticfiles\management\commands\runserver.py", line 27, in get_handler
        handler = super().get_handler(*args, **options)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 65, in get_handler
        return get_internal_wsgi_application()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\servers\basehttp.py", line 47, in get_internal_wsgi_application
        raise ImproperlyConfigured(
    django.core.exceptions.ImproperlyConfigured: WSGI application 'backend.wsgi.application' could not be loaded; Error importing module.
    

    Thank you for the tutorial, but I think it is too much copy paste without explanation. Without solutions in the comments, it would not work at all. While I’ve now built an app using Django and React, I do not feel like I learned very much in the process.

    Thank you for the tutorial, but I think it is too much copy paste without explanation. Without solutions in the comments, it would not work at all. While I’ve now built an app using Django and React, I do not feel like I learned very much in the process.

    For me, the best tutorial Django and React from web.Thanks! The Digital Ocean tutorials are the best! always work for me! thank u very much

    I was getiing 403 errors and fixed it by using 127.0.0.1:3000 AND I used Andrewd’s solution but i’d still like to use localhost:3000 or get it working too…

    can someone help me with the deployment of this app

    I haven’t even got that far and I have run into this error, can anyone help?

    Just after i completed step 2 i got this when going on localhost:8000/todos

    Error: OperationalError at /api/todos/ no such table: todo_todo

    Except at Initial steps regarding creating a virtual environment. This is a perfect post for beginners and everything working fine

    My name is Alauddin. I am learning to react and Django together. I got a valuable article for myself. I am trying to do this. If I get any error I will surely ask you. Thank you for sharing.

    POST http://localhost:8000/api/todos/ 400 (Bad Request)

    got this error, probably some problem in my Django app, any help would be appreciated, thanks in advance :)

    Bobby Iliev
    Site Moderator
    Site Moderator badge
    August 4, 2021

    Hi there,

    In your ALLOWED_HOSTS make sure to specify the following:

    ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'your_domain']
    

    Hi @bobbyiliev actually I tried putting the hosts you mentioned and I also tried doing ALLOWED_HOSTS = [‘*’], but no success same error still…

    Bobby Iliev
    Site Moderator
    Site Moderator badge
    August 8, 2021

    Hi there @metkar9699130773,

    In this case, I could suggest enabling Debug mode to see if you could get some more information about the problem rather than the 400 error.

    Let me know how it goes!

    After setting up back-end and creating api ! when i restart server. I get this

    System check identified no issues (0 silenced).
    August 09, 2021 - 07:22:02
    Django version 3.2.6, using settings 'backend.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    Exception in thread django-main-thread:
    Traceback (most recent call last):
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\servers\basehttp.py", line 45, in get_internal_wsgi_application
        return import_string(app_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\module_loading.py", line 17, in import_string
        module = import_module(module_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\importlib\__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 855, in exec_module
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File "D:\Coding\React\django-todo-react\backend\backend\wsgi.py", line 16, in <module>
        application = get_wsgi_application()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\wsgi.py", line 13, in get_wsgi_application
        return WSGIHandler()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\handlers\wsgi.py", line 127, in __init__
        self.load_middleware()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\handlers\base.py", line 40, in load_middleware
        middleware = import_string(middleware_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\module_loading.py", line 17, in import_string
        module = import_module(module_path)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\importlib\__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
    ModuleNotFoundError: No module named 'corshaders'
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\threading.py", line 954, in _bootstrap_inner
        self.run()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\threading.py", line 892, in run
        self._target(*self._args, **self._kwargs)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
        fn(*args, **kwargs)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 138, in inner_run
        handler = self.get_handler(*args, **options)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\contrib\staticfiles\management\commands\runserver.py", line 27, in get_handler
        handler = super().get_handler(*args, **options)
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\management\commands\runserver.py", line 65, in get_handler
        return get_internal_wsgi_application()
      File "C:\Users\Mega\AppData\Local\Programs\Python\Python39\lib\site-packages\django\core\servers\basehttp.py", line 47, in get_internal_wsgi_application
        raise ImproperlyConfigured(
    django.core.exceptions.ImproperlyConfigured: WSGI application 'backend.wsgi.application' could not be loaded; Error importing module.
    

    Thank you for the tutorial, but I think it is too much copy paste without explanation. Without solutions in the comments, it would not work at all. While I’ve now built an app using Django and React, I do not feel like I learned very much in the process.

    Thank you for the tutorial, but I think it is too much copy paste without explanation. Without solutions in the comments, it would not work at all. While I’ve now built an app using Django and React, I do not feel like I learned very much in the process.

    For me, the best tutorial Django and React from web.Thanks! The Digital Ocean tutorials are the best! always work for me! thank u very much

    I was getiing 403 errors and fixed it by using 127.0.0.1:3000 AND I used Andrewd’s solution but i’d still like to use localhost:3000 or get it working too…

    can someone help me with the deployment of this app

    Pretty good project. Thanks for sharing. A couple of things that needed more explanation but was fun to find the solutions. Thanks again!

    Excellent tutorial!

    In case you’re having issues with submitted todos actually showing up in the app, the proxy might need to be reconfigured

    proxy: "http://localhost:8000/

    in my case it the correct proxy actually “http://127.0.0.1:8000/

    To find this look up the printout from the Terminal running the backend:

    System check identified no issues (0 silenced). Django version 4.1.5, using settings ‘backend.settings’ Starting development server at http://127.0.0.1:8000/

    Hope this helps!

    Perfect. It did helped me. Thank you all.

    This is my error request failed with status code 404 someone help me greetings image in the link thank you

    https://drive.google.com/file/d/1TmNQgLI8XprNeBYwsIynKfOd-udUQir2/view?usp=share_link

    Try DigitalOcean for free

    Click below to sign up and get $200 of credit to try our products over 60 days!

    Sign up

    Join the Tech Talk
    Success! Thank you! Please check your email for further details.

    Please complete your information!

    Become a contributor for community

    Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

    DigitalOcean Documentation

    Full documentation for every DigitalOcean product.

    Resources for startups and SMBs

    The Wave has everything you need to know about building a business, from raising funding to marketing your product.

    Get our newsletter

    Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

    New accounts only. By submitting your email you agree to our Privacy Policy

    The developer cloud

    Scale up as you grow — whether you're running one virtual machine or ten thousand.

    Get started for free

    Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

    *This promotional offer applies to new accounts only.