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:
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.
To follow along with this tutorial, you will need to:
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.
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:
Next, navigate into the directory:
Now install Pipenv using pip
:
Note: Depending on your installation, you may need to use pip3
instead of pip
.
And activate a new virtual environment:
Install Django using Pipenv:
Then create a new project called backend
:
Next, navigate into the newly created backend directory:
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:
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
).
todo
ApplicationNow 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
:
Then, save your changes.
Todo
ModelLet’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:
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:
And apply the changes to the database:
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:
Then, save your changes.
You will need to create a “superuser” account to access the admin interface. Run the following command in your terminal:
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:
Navigate to http://localhost:8000/admin
in your web browser. And log in with the username and password that was created earlier:
You can create, edit, and, delete Todo
items using this interface:
After experimenting with this interface, you can stop the server (CONTROL+C
or CTRL+C
).
In this section, you will create an API using the Django REST framework.
Install the djangorestframework
and django-cors-headers
using Pipenv:
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:
Then, add these lines of code to the bottom of the backend/settings.py
file:
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.
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:
This code specifies the model to work with and the fields to be converted to JSON.
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:
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:
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:
Navigate to http://localhost:8000/api/todos
in your web browser:
You can CREATE
a new Todo item using the interface:
If the Todo item is created successfully, you will be presented with a successful response:
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:
This completes the building of the backend of the application.
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:
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:
Then, start the application:
Your web browser will open http://localhost:3000
and you will be presented with the default Create React App screen:
Next, install bootstrap
and reactstrap
to provide user interface tools.
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
:
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:
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:
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:
Then, create a Modal.js
file and open it with your code editor. Add the following lines of code:
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:
activeItem
represents the Todo item to be edited.toggle
is a function used to control the Modal’s state (i.e., open or close the modal).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:
Save your changes and observe the application in your web browser:
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:
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
:
Then open the frontend/package.json
file in your code editor and add a proxy
:
[...]
"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:
With proxy
, you can provide relative paths:
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:
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:
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:
Now when you visit http://localhost:3000
with your web browser, your application will allow you to READ
, CREATE
, UPDATE
, and DELETE
tasks.
This completes the frontend and backend of the Todo application.
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.
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
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
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.
You added localhost:8000 as a proxy and you mentioned the purpose of it. However, you still left it in your axios requests
I believe you’ll be getting forbidden requests unless you include CSRF tokens as well.
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:
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:
otherwise bootstrap wouldn’t load.