Как создать CRUD-приложение на ReactJS и Django Rest Framework
В этом посте я создаю простое CRUD-приложение с помощью ReactJs и Django Rest Framework. Мы разделим этот пост на две части - Frontend
и Backend
.
Во фронтенд-части мы будем использовать ReactJS для работы на стороне клиента и вызова API. И наоборот, в бэкенд-части для создания API будет использоваться фреймворк Django Rest.
Перед началом обучения нам необходимо установить некоторые пакеты/библиотеки в папки фронтенда и бэкенда.
Для фронтенда нам необходимо установить:
- axios - для вызовов API
- react-router-dom - для маршрутизации между страницами
Для бэкенда нам необходимо установить:
Фронтенд
App.js
import './App.css';
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom';
import Home from './Components/Home';
import About from './Components/About';
import Contact from './Components/Contact';
import Edit from './Components/Edit';
import Root from './Components/Root';
function App() {
const url = "http://127.0.0.1:8000/"
const router = createBrowserRouter(createRoutesFromElements(
<Route path="/" element={<Root/>}>
<Route index element={<Home url={url}/>}/>
<Route path="/about" element={<About/>}/>
<Route path="/contact" element={<Contact/>}/>
<Route path="/edit/:id/" element={<Edit url={url}/>}/>
</Route>
))
return (
<div>
<RouterProvider router={router}/>
</div>
);
}
export default App;
Components/Root.js
import { Link, Outlet } from "react-router-dom";
function Root(){
return(
<>
<nav className="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div className="container-fluid">
<a className="navbar-brand" href="#">React+DRF</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<Link to='/' className="nav-link active" aria-current="page" href="#">Home</Link>
</li>
<li className="nav-item">
<Link to='/about' className="nav-link active" href="#">About</Link>
</li>
<li className="nav-item">
<Link to='/contact' className="nav-link active" href="#">Contact</Link>
</li>
</ul>
</div>
</div>
</nav>
<div>
<Outlet/>
</div>
</>
)
}
export default Root;
Components/Home.js
import axios from "axios";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
function Home(props){
useEffect(()=>{
getData();
},[])
const [users, setUsers] = useState([]);
const [username, setUsername] = useState();
const [password, setPassword] = useState();
// Fetching the data from DRF API
async function getData(){
const response = await axios.get(props.url);
setUsers(response.data);
}
// Posting the data to DRF API
async function handleSubmit(e){
e.preventDefault();
let data = {
user_name: username,
password: password
}
await axios.post(props.url+'create_user/', data, {
headers:{
'Content-Type': 'multipart/form-data'
}
});
getData();
}
//Delete a specific user
const deleteUser=(user_id)=>{
axios.delete(props.url+`delete_user/${user_id}/`, {id:user_id})
.then(()=>getData())
.catch((err)=>console.log(err))
}
return(
<div className="container w-50 my-5">
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="exampleFormControlInput1" className="form-label">Username</label>
<input type="text" className="form-control" id="exampleFormControlInput1" value={username} onChange={(e)=>setUsername(e.target.value)} placeholder="Username"/>
</div>
<div className="mb-3">
<label htmlFor="exampleFormControlInput2" className="form-label">Password</label>
<input type="password" className="form-control" id="exampleFormControlInput2" value={password} onChange={(e)=>setPassword(e.target.value)} placeholder="Password"/>
</div>
<input type="submit" className="btn btn-primary form-control" value="ADD"/>
</form>
<br/>
<table className="table text-center" border={1} cellPadding={5}>
<tbody>
<tr>
<th>Id</th>
<th>Username</th>
<th>Password</th>
<th>Operation</th>
</tr>
{
users.map((user=>{
return(
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.user_name}</td>
<td>{user.password}</td>
<td>
<Link className="btn btn-sm btn-primary mx-3" to={`/edit/${user.id}`} state={{uname: user.user_name, pwd: user.password}}>Edit</Link>
<button className="btn btn-sm btn-danger" onClick={()=>deleteUser(user.id)}>Delete</button>
</td>
</tr>
)
}))
}
</tbody>
</table>
</div>
)
}
export default Home;
Components/Edit.js
import axios from "axios";
import { useState } from "react";
import { useLocation, useParams, useNavigate } from "react-router-dom";
function Edit(props) {
const location = useLocation();
const userId = useParams()
const [username, setUsername] = useState(location.state.uname);
const [password, setPassword] = useState(location.state.pwd);
const navigate = useNavigate();
const updateUser = async (e) =>{
e.preventDefault()
const response = await axios.put(props.url+`update_user/${userId.id}/`, {user_name: username, password: password});
navigate("/")
}
return(
<div className="container w-50 my-5">
<form onSubmit={updateUser}>
<div className="mb-3">
<label for="exampleFormControlInput1" className="form-label">Username</label>
<input type="text" className="form-control" id="exampleFormControlInput1" value={username} onChange={(e)=>setUsername(e.target.value)} placeholder="Username"/>
</div>
<div className="mb-3">
<label for="exampleFormControlInput2" className="form-label">Password</label>
<input type="password" className="form-control" id="exampleFormControlInput2" value={password} onChange={(e)=>setPassword(e.target.value)} placeholder="Password"/>
</div>
<input type="submit" className="btn btn-primary form-control" value="Update"/>
</form>
</div>
)
}
export default Edit;
Components/About.js
function About(){
return(
<h1>About page</h1>
)
}
export default About;
Components/Contact.js
function Contact(){
return(
<h1>Contact page</h1>
)
}
export default Contact;
Бэкенд
settings.py
Мы должны создать приложение main
в нашем проекте и добавить main
, rest_framework
и corsheaders
в INSTALLED_APPS
, а также добавить MIDDLEWARE
в settings.py
.
INSTALLED_APPS = [
.
.
'main',
'rest_framework',
'corsheaders'
]
CORS_ALLOW_ALL_ORIGINS=True
MIDDLEWARE = [
.
.
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
project's urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('main.urls')),
]
models.py
Создайте модель MyUser
, содержащую user_name
и password
.
from django.db import models
class MyUser(models.Model):
user_name = models.CharField(max_length=50)
password = models.CharField(max_length=50)
def __str__(self):
return self.user_name
admin.py
Зарегистрируйте указанную модель в файле admin.py
следующим образом:
from django.contrib import admin
from .models import MyUser
admin.site.register(MyUser)
serializers.py
from rest_framework import serializers
from .models import MyUser
class MyUserSerializer(serializers.ModelSerializer):
class Meta:
model = MyUser
fields = "__all__"
views.py
from .models import MyUser
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import MyUserSerializer
@api_view(['GET'])
def getData(request):
users = MyUser.objects.all()
users_serializer = MyUserSerializer(users, many=True)
return Response(users_serializer.data)
@api_view(['POST'])
def create_user(request):
if request.method=='POST':
data=request.data
serializer = MyUserSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response("Errrro")
@api_view(['PUT'])
def update_user(request, id):
if request.method=='PUT':
data=request.data
user_obj = MyUser.objects.get(id=id)
serializer = MyUserSerializer(user_obj, data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response("Errrro")
@api_view(['DELETE'])
def delete_user(request, id):
if request.method=='DELETE':
data=request.data
user_obj = MyUser.objects.get(id=id)
user_obj.delete()
return Response("Delete succesffully")
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.getData),
path('create_user/', views.create_user),
path('update_user/<int:id>/', views.update_user),
path('delete_user/<int:id>/', views.delete_user),
]
Для запуска react-сервера необходимо открыть каталог frontend
в cmd и выполнить команду npm start
.
Для запуска сервера backend
необходимо открыть директорию backend
в cmd и выполнить следующую команду - python manage.py runserver
.