Connect a React app into Django website's API
24.07.2024
15.04.2025
14 minutes
70
0
0
0
0
Introduction
In this article, I will describe a process of integrating the React framework into the Django website. We will configure a communication API between both of them. Also, a TailwindCSS library will be connected for further simplification of website building.
All source code can be found on github. The name of the branch is simple-integration-react-django https://github.com/DmRafaule/SearchResultParser/tree/simple-integration-react-django
Creation of a basic django project
The root directory of a project will be called SearchResultParser. And inside of it, you need to create your django project with the same name, SearchResultParser. After installing a basic project, we may continue to create the first app for this project.
Creation of a basic django app
Before you jump by hyperlink in chapter name to other article, you must know, my app is going to be called Main, and it will have django model called Result; for the admin page model, it will be called ResultAdmin; and you will have to add the next fields:
- user (CharField)
- file (FileField)
- time_created (DateTimeField)
Setting up an API for communication between React and Django
Installing API
pip install djangorestframework django-cors-headers
In a file settings.py, there is a need to add the next apps, coreheaders, and rest_framework into INSTALLED_APP. Also, you need to add one middleware, corsheaders.middleware.CorsMiddleware into MIDDLEWARE list.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'rest_framework',
'Main.apps.MainConfig',
]
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',
]
Now, add next lines into settings.py
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000'
]
Creating a serializer for a model
For the frontend part of the website to work with django models, we must get them in some ways. For that reason, we will create a serializer(exporter) which is going to convert django models into json file responses.
Create a new file in Main directory.
cd Main
touch serializers.py
Paste it into that file:
from rest_framework import serializers
from .models import Result
class ResultSerializer(serializers.ModelSerializer):
class Meta:
model = Result
fields = (‘id’, 'user', 'file', 'time_created')
Preparing and setting up APIs
In a file Main/views.py, let’s add a new view for API calls. A file will have next content:
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import ResultSerializer
from .models import Result
class ResultView(viewsets.ModelViewSet):
serializer_class = ResultSerializer
queryset = Result.objects.all()
def home(request):
return render(request, 'Main/home.html')
In a file SearchResultParser/urls.py, we must create a router and add new paths. New content of the file:
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from Main import views
router = routers.DefaultRouter()
router.register(r'results', views.ResultView, 'result')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('', include('Main.urls'))
]
After all of those manipulations, you will have the next working URLs patterns:
- localhost:8000/api/results – Here you can see all records in your database.
- localhost:8000/api/results/1/ - Here you can see data in a specified record by typing an ID
Creating base project in React
I’ve created a react project in the root directory, where you have .venv folder, and call it searchresultparser-frontend.
Installing and setting up a TailwindCSS library in a project (optional)
It is optional. If you think you have a better library to use and manage CSS, go for it.
Entering a react project folder:
cd searchresultparser-frontend
Install the new package:
npm install -D tailwindcss
Create a configuration file (tailwind.config.js) and a little bit change it.
npx tailwindcss init
Replace the inner content of searchresultparser-frontend/tailwind.config.js by:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Now add a basic directives in a searchresultparser-frontend/src/index.css file
@tailwind base;
@tailwind components;
@tailwind utilities;
Build and run:
npm run start
Now the Tailwindcss library is ready for use.
Creating a basic interface on React
Modal window
In a directory searchresultparser-frontend/src/, create a new file:
touch Modal.js
This file is describing how a modal window will appear and what it will looks like. This UI element is going to be helpful in the future in the case of sending requests via forms and the API of our website. Paste the next content in a Modal.js file:
import React from "react";
const Modal = ({ isOpen, onClose, children }) => {
if (!isOpen) return null;
return (
{children}
Close
);
};
export default Modal;
Webpage of the app
The next ‘piece’ of code describing a basic interface for communication with a server and execution of such basic operations as CRUD. Open an App.js file and replace it with this:
import React, { useState } from 'react';
import Modal from './Modal';
var items =[
{
id: 1,
user: "dima",
file: "0020kfjewl01232",
time_created: "11.07.24"
},
{
id: 2,
user: "artur",
file: "ert0k355wl01232",
time_created: "12.07.24"
},
{
id: 3,
user: "pasha",
file: "aaaakfje23ddf32",
time_created: "13.07.24"
},
];
let currentChildren = null
function App() {
const [isModal, setOpen] = React.useState(false);
const renderItems = () => {
return items.map((item) => (
{item.user+'__'+item.file}
editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit
deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete
));
}
const handleClose = () => {
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
};
const deleteItem = (item) => {
handleOpen();
currentChildren =
DELETE ITEM
Are you sure ?
};
const createItem = () => {
handleOpen();
currentChildren =
ADD NEW ITEM
};
const editItem = (item) => {
handleOpen();
currentChildren =
EDIT ITEM
};
return (
Add new item
{renderItems()}
);
}
export default App;
Once again, do not bother to investigate that piece of code; it is going to be replaced in a next chapter. Just like that.
Combining React and Django
Axios or fetch ?
After finishing up with the backend and frontend parts we must combine them in some way. For that matter, I suggest using javascript library axios.
Why not fetch, but axios ?
I personally choose axios because of syntax; it is just less code to type. And one more reason he supports older browsers and older versions of them. So … Here I will attach a table for comparison between them; for that reason you could decide for yourself, what best fits you. Also, you will see in the next chapters of this article examples of usage for both of them.
Feature | fetch | axios |
---|---|---|
Availability | Built-in | Requires installation (axios package) |
Syntax | Verbose, more boilerplate | Concise, user-friendly |
JSON Handling | Manual parsing with .json() | Automatic parsing |
Error Handling | Requires manual handling | Better built-in error handling |
Interceptors | Not available | Available |
Request Cancellation | Not available | Available |
Browser Support | Modern browsers only | Better compatibility with older browsers |
Let’s install and set up an axios library
npm install axios
After installing, in a package.json file you may add a proxy for a server. Insert it:
"proxy": "http://localhost:8000",
It is optional, but as a benefit, when we will be making requests, we no longer have to type the full address of a server. Less code, less errors.
Now all that remains is to decide where and from where we are going to access the API of our application.
Changing the code of App.js step-by-step
Imports and variables
Firstly, let’s add function useEffect in import. Secondly, let’s import the axios library. Thirdly, delete the items list. At the end, we will have only 3 imports, just like that:
import React, { useEffect, useState, useCallback } from 'react';
import Modal from './Modal';
import axios from "axios";
All of the remaining pieces of code will be pasted into an app function
function App() {
…
}
At the beginning of a function, declare all needed variables as a React state. With callbacks to change their states as it will be needed later.
const [isModal, setOpen] = useState(false);
const [isUpdate, setUpdateTrigger] = useState(true);
const [itemsData, setItemsData] = useState(null);
const [formData, setFormData] = useState({
username: "",
datetime: "",
file: null,
})
const [formBody, setFormBody] = useState(null);
Modal window
For opening and closing a modal window, let’s add the next functions:
const handleClose = () => {
setOpen(false);
};
const handleOpen =() => {
setOpen(true);
};
They are working in such a way that changing a isModal variable will cause visibility of the modal window.
List rendering
Let’s paste a code that is going to render our list of data accepted from a server. This code is going to be pasted at the end of the app function.
return (
Add new item
{/* Place for a list of displaying items */}
{itemsData ? (itemsData.map((item) => (
{item.user+'__'+item.file}
editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit
deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete
))) : (Loading...
)
}
{/* Place for modal window */}
);
Rendering modal window, CRUD operations
As you can see in the rendering template above, there are functions such as:
- createItem
- editItem
- deleteItem
The idea of those functions is simple. While pushing one of the buttons, which has a corresponding callback, they are going to paste a specific form`s code into the Modal component. Forms code is depending on what kind of operation you want to proceed with.
const deleteItem = (item) => {
handleOpen();
setFormBody(
DELETE ITEM
Are you sure ?
)
};
const createItem = () => {
handleOpen();
setFormBody(
ADD NEW ITEM
)
};
const editItem = (item) => {
handleOpen();
setFormBody(
EDIT ITEM
)
};
You should paste them somewhere above the return keyword.
Refreshing forms data
In my forms, the handleChange function is responsible for updating the data.
const handleChange = (e) => {
if (e.target.name === 'file') {
formData.file = e.target.files[0]
setFormData(formData)
} else if (e.target.name === 'username') {
formData.username = e.target.value
setFormData(formData)
} else if (e.target.name === 'date') {
formData.datetime = e.target.value
setFormData(formData)
}
}
Communication with a server
The next functions are corresponding for sending requests to the server:
- editingSubmit
- creationSubmit
- deletionSubmit
And basically they are using the axios library of fetch API
The createSubmit function sends POST requests to /api/results/. It saves and sends the entered data, and at the end of execution it starts updating the data list.
const creationSubmit = (form_el) =>{
form_el.preventDefault()
var form_data = new FormData();
form_data.append("user", formData.username)
form_data.append("time_created", formData.datetime)
form_data.append("file", formData.file)
// Use the fetch API to send the form data to the server
axios.post('/api/results/', form_data)
.then(data => {
console.log('Success:', data);
setUpdateTrigger(true)
})
.catch(error => {
console.error('Error:', error);
});
handleClose()
};
const creationSubmit = (form_el) =>{
form_el.preventDefault()
var form_data = new FormData();
form_data.append("user", formData.username)
form_data.append("time_created", formData.datetime)
form_data.append("file", formData.file)
fetch(`http://localhost:8000/api/results/`,{
method: 'POST',
headers: {
'Accept': 'application/json',
},
body: form_data
})
.then( response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
console.log('Success');
setUpdateTrigger(true)
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
handleClose()
};
The editingSubmit function sends PUT requests to /api/results/{ID} . It saves and sends the entered data, and at the end of execution it starts updating the data list.
const editingSubmit = (form_el) => {
console.log(form_el)
form_el.preventDefault()
var form_data = new FormData();
form_data.append("user", formData.username)
form_data.append("time_created", formData.datetime)
form_data.append("file", formData.file)
// Use the fetch API to send the form data to the server
axios.put(`/api/results/${form_el.target.id}/`, form_data)
.then(data => {
console.log('Success:', data);
setUpdateTrigger(true)
})
.catch(error => {
console.error('Error:', error);
});
handleClose()
}
const editingSubmit = (form_el) => {
console.log(form_el)
form_el.preventDefault()
var form_data = new FormData();
form_data.append("user", formData.username)
form_data.append("time_created", formData.datetime)
form_data.append("file", formData.file)
fetch(`http://localhost:8000/api/results/${form_el.target.id}/`,{
method: 'PUT',
headers: {
'Accept': 'application/json',
},
body: form_data
})
.then( response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
console.log('Success');
setUpdateTrigger(true)
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
handleClose()
}
The deletionSubmit is sending DELETE requests to /api/results/{ID} . And because of that, removing data records by ID. At the end of execution, it starts updating the data list.
const deletionSubmit = (item) => {
axios.delete(`/api/results/${item.id}/`)
.then((res) => setUpdateTrigger(true));
handleClose()
}
const deletionSubmit = (item) => {
fetch(`http://localhost:8000/api/results/${item.id}/`,{
method: "DELETE",
headers: {
'Accept': 'application/json',
}
})
.then( response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
setUpdateTrigger(true)
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
handleClose()
}
Data list refreshing
Up to this point, I wrote about how functions trigger list updates. What's the matter. The problem is in the useEffect function. In my case, this function calls handleUpdate only when the isUpdate state variable (see above) has changed. And it changes in the functions of deletion, changing, and adding records to the database.
const handleUpdate = () => {
axios.get('/api/results/')
.then(response => {
setItemsData(response.data); // Save the data in state
})
.catch(error => {
console.error('There was an error fetching the data!', error);
});
setUpdateTrigger(false)
};
const handleUpdate = () => {
fetch('http://localhost:8000/api/results/',{
method: 'GET',
headers: {
'Accept': 'application/json',
},
})
.then( response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
const dataArray = Array.isArray(data) ? data : [data];
setItemsData(dataArray); // Save the data in state
})
.catch(error => {
console.error('There was a problem with your fetch operation:', error);
});
setUpdateTrigger(false)
};
Insert the useEffect function before the beginning of a return keyword.
useEffect(handleUpdate, [isUpdate]);
If you use the useEffect function without a list of dependencies (i.e., isUpdate in our case), react will send requests to update the list constantly, which of course puts a lot of pressure on the server.
That's all for today. The React application is ready and is successfully communicating with the website backend.
Full source code for App.js
Conclusion
Of course, I didn’t talk about how to deploy this project to the server, or how react will interact with django templates. Of course, there will be separate articles for them and everything will settle down over time. That’s all for today.
All source code can be found on github, name of branch is simple-integration-react-django https://github.com/DmRafaule/SearchResultParser/tree/simple-integration-react-django
Comments
(0)
Send
It's empty now. Be the first (o゚v゚)ノ
Other
Similar articles
Used termins
- Hosting ⟶ It is a service for providing space on a server and allocating predetermined resources to this server, with the condition that this server has permanent access to the Internet.
- CORS (Cross-Origin Resource Sharing) ⟶ Is a security feature implemented in web browsers that enables controlled access to resources located outside of a given origin (domain, protocol, and port combination). It is a mechanism that allows web servers to specify who can access their resources and which HTTP methods are permitted for cross-origin requests.
- IP (Internet protocol) ⟶ A set of rules governing the format of data sent over the Internet or other network. IP is responsible for addressing and routing packets of data so they can travel across networks and reach the correct destination. The most common versions are IPv4 and IPv6.
- TCP (Transmission Control Protocol,) ⟶ Is one of the main protocols of the Internet Protocol Suite. It is used for establishing a connection between networked devices, ensuring reliable data transmission over the internet or other networks.
Related questions
- My App doesn't render correctly on the server? If it doesn't work, in 99% of cases it's a configuration issue. A missing property, a wrong call order, or a missing component – server-side rendering is strict about configuration. The best way to find out what's wrong is to compare your project to an already working setup. Check out the reference implementations, bit by bit.
- I can’t stand Django template language. Do I have to use it? I think this template engine is the best thing ever, but I know that choosing a template language runs close to religion. There’s nothing about Django that requires using the template language, so if you’re attached to Jinja2, Mako, or whatever it's ok.
- How can I see the raw SQL queries Django is running? Make sure your Django DEBUG setting is set to True. Then import connection from django.db. connection.queries is only available if DEBUG is True. It’s a list of dictionaries in order of query execution. Each dictionary has the sql and time property.
- If I make changes to a model, how do I update the database? Take a look at Django’s support for schema migrations. If you don’t mind clearing data, your project’s manage.py utility has a flush option to reset the database to the state it was in immediately after migrate was executed.