3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер

3 горизонтальные линии, бургер
Удалить все
ЗАГРУЗКА ...

Содержание



    Django REST API в React приложении. Как сделать и как использовать

    Часы
    24.07.2024
    /
    Часы
    02.10.2025
    /
    Часы
    13 минут
    Глазик
    757
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0

    Введение

    В данной статье я опишу процесс объединения React и Django под одним проектом. Мы настроим API для общения обоих между собой, а так же подключим TailwindCSS библиотеку, дабы упростить создание сайта ещё сильнее.
    Весь код статьи можно скачать с github, ветка называется simple-integration-react-django https://github.com/DmRafaule/SearchResultParser/tree/simple-integration-react-django

    Создание базового проекта на Django

    Корневая директория будет называться SearchResultParser. В ней создайте новый django проект. Он тоже будет иметь название SearchResultParser. После установки базового проекта, можно перейти к созданию первого приложения для данного проекта.

    Добавление приложения к Django проекту

    Игнорируй всё ниже описанное в данной главе, если ты хочешь просто подключить react к django и создать своё приложение.
    Перед тем как ты перейдёшь по ссылке в заголовке, ты должен знать, что название у приложения будет Main, а название модели в ней будет Result, для админки модель будет называться ResultAdmin. И ты должен будешь добавить следующие поля в неё:
    • user (CharField)
    • file (FileField)
    • time_created (DateTimeField)

    Настройка API для общения между React и Django

    Установка APIs

    pip install djangorestframework django-cors-headers
    В файле settings.py, необходимо будет добавить ново установленные приложения, coreheaders и rest_framework в INSTALLED_APP, а так же добавить одну мидлвари corsheaders.middleware.CorsMiddleware в MIDDLEWARE список.
    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', ]
    Добавь следующие строчки внизу файла settings.py
    CORS_ORIGIN_WHITELIST = [ 'http://localhost:3000' ]

    Создание cериализатора для модели

    Чтобы фронтенд мог работать с моделями, необходимо их как-то получать. Для этого я создам сериализатор(экспортёр), который превратит отправляемые бэкендом записи баз данных, в json файлы.
    Создай новый файл в директории приложения Main.
    cd Main touch serializers.py
    Добавь следующий контент:
    from rest_framework import serializers from .models import Result class ResultSerializer(serializers.ModelSerializer):     class Meta:         model = Result         fields = (‘id’, 'user', 'file', 'time_created')

    Подготовка и настройка APIs

    В файле Main/views.py добавим новое представление для API. Файл будет выглядеть так:
    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')
    В файле SearchResultParser/urls.py нужно будет создать роутер и подключить новые пути. Вот новое содержимое файла:
    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')) ]
    После всего этого у тебя появятся следующие адреса для навигации:
    • localhost:8000/api/results – Здесь ты можешь просмотреть все записи в твоей базе данных
    • localhost:8000/api/results/1/ - Так ты можешь просмотреть одну единственную запись, 1 это id записи

    Создание базового проекта для React

    Я создал react проект в корневой директории, где .venv и назвал его searchresultparser-frontend.

    Устанавливаем и подключаем TailwindCSS к проекту (опционально)

    Опционально, если хотите использовать что-нибудь другое. Заходим в директорию react проекта:
    cd searchresultparser-frontend
    Установим пакет
    npm install -D tailwindcss
    Создадим файл конфигурации (tailwind.config.js) и отредактируем его
    npx tailwindcss init
    Замени содержание файла searchresultparser-frontend/tailwind.config.js на
    /** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [], }
    Добавим базовые директивы в searchresultparser-frontend/src/index.css
    @tailwind base; @tailwind components; @tailwind utilities;
    Начнём сборку и запустим проект
    npm run start
    Теперь библиотека tailwindcss готова к использованию.

    Создадим базовый интерфейс на React

    Модальное окно

    В директории searchresultparser-frontend/src/ создайте новый файл.
    touch Modal.js
    Данный файл описывает, появление и вид модального окна. Которое нам потребуется в будущем для отправки различных запросов к API нашего сайта. Вставьте следующий код в файл Modal.js:
    import React from "react";   const Modal = ({ isOpen, onClose, children }) => {     if (!isOpen) return null;     return (         <div className="fixed top-0 left-0 w-full h-full bg-opacity-50 flex items-center justify-center">             <div className="bg-white h-auto  min-w-80  p-3 rounded border-2 z-10 gap-3">                 {children}                 <div className="flex justify-end  mr-2">                     <div onClick={onClose} className="bg-red-400 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Close</div>                 </div>             </div>         </div>     ); };   export default Modal;

    Страница приложения

    Следующий «кусочек» кода описывает базовый интерфейс для общения с сервером и выполнения таких базовых операций как CRUD. Откройте файл App.js и вместо кода по умолчанию вставьте следующий код:
    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) => (         <div className="flex flex-row pl-4 h-8 border-2 rounded bg-amber-100">         <div className="flex-grow">           {item.user+'__'+item.file}         </div>         <div className="flex">           <div onClick={()=> editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit</div>           <div onClick={()=> deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete</div>         </div>       </div>     ));   }   const handleClose = () => {     setOpen(false);   };     const handleOpen = () => {     setOpen(true);   };   const deleteItem = (item) => {     handleOpen();     currentChildren =     <div className='flex flex-col justify-between h-full gap-9'>       <b className='flex w-full justify-center text-lg'>         DELETE ITEM       </b>         <div className='flex w-full justify-center'>         Are you sure ?       </div>       <div className='flex w-full justify-center gap-2'>         <button className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Yes</button>         <button className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>No</button>       </div>     </div>   };     const createItem = () => {     handleOpen();     currentChildren =       <div>         <b className='flex w-full justify-center text-lg'>           ADD NEW ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form className='flex flex-col gap-3'>               <input type="text" name="username" placeholder="Name" maxlength="25" minlength="3" required="true" id="id_username"/>               <input type="datetime-local" name="date" placeholder="Time" required="true" id="id_date"/>               <input type="file" name="file" placeholder="File" required="true" id="id_file"/>           </form>         </div>         <div className='flex w-full justify-end gap-2'>           <button className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Send</button>         </div>       </div>   };     const editItem = (item) => {     handleOpen();     currentChildren =       <div>         <b className='flex w-full justify-center text-lg'>           EDIT ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form className='flex flex-col gap-3'>               <input type="text" name="username" placeholder="Name" maxlength="25" minlength="3" required="true" id="id_username" value={item.user} />               <input type="datetime-local" name="date" placeholder="Time" required="true" id="id_date" value={item.time_created} />               <input type="text" name="file" placeholder="File" required="true" id="id_file" value={item.file} />           </form>         </div>         <div className='flex w-full justify-end gap-2'>           <button className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Save</button>         </div>       </div>   };   return (     <div className="p-10 flex flex-col gap-7 ">       <header className="flex flex-row ">         <div onClick={createItem} className="pt-2 pb-2 pl-3 pr-3 border-2 rounded hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Add new item</div>       </header>       <main className=" flex flex-col flex-auto flex-wrap gap-2">         {renderItems()}       </main>       <Modal isOpen={isModal} onClose={handleClose} children={currentChildren}>               </Modal>     </div>   ); } export default App;
    Опять же, не особо зацикливайтесь на данном «кусочке» кода, мы его всё равно в следующей главе будем менять и препарировать. Вот так.

    Соединение React и Django

    Axios или fetch ?

    После того как с фронтендом и бэкендом было покончено, нам необходимо их как-то связать. Для это предлагаю использовать javascript библиотеку axios.
    Почему не fetch, а axios ?
    Лично я выбрал axios потому что мне понравился его синтаксис, он просто элегантней и ещё одно, он поддерживает старые браузеры и их версии. Вот. Но я так же прикреплю таблицу их сравнения, что бы вы смогли сами решить что из этого лучше.
    Фичи fetch axios
    Доступность Встроенный Требуется установка
    Синтакс Подробный, более шаблонный Краткий, удобный для пользователя
    Обратка JSON Ручная Автоматическая
    Обработка ошибок Ручная Встроенная
    Наличие перехватчиков Не доступно Доступно
    Закрытие запросов Не доступно Доступно
    Поддержка браузеров Только современные браузеры Поддержка старых версий доступна
    Установим и настроим axios.
    npm install axios
    После установки в файл package.json можно добавить путь до нашего сервера. Вставьте это значение:
    "proxy": "http://localhost:8000",
    Это вообще не обязательно, но позволяет не писать лишний раз полный адрес вашего сервера. Меньше кода, меньше ошибок.
    Теперь осталось только указать, где и откуда мы собираемся обращаться к API нашего приложения.

    Пошагово, с комментариями меняем код для App.js

    Импорты и переменные состояния

    Во-первых, импортируем функцию из react: useEffect . Во-вторых, импортируем сам axios. В-третьих, уберём var items. Итого, до определения функции App у нас будет только 3 импорта, вот так:
    import React, { useEffect, useState, useCallback } from 'react'; import Modal from './Modal'; import axios from "axios";
    Все последующие куски кода будут вставляться в функцию App
    function App() { … }
    В начале, объявляем необходимые переменные и привязываем к ним функции для их изменения.
    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);

    Модальное окно

    Для появления и скрытия модального окна, добавим следующие функции:
    const handleClose = () => {   setOpen(false); }; const handleOpen =() => {   setOpen(true); };
    Они работают таким образом, что при изменении переменной isModal, модальное окно будет либо появляться, либо исчезать.

    Отрисовка списка

    Давай сразу вставим код, который будет отрисовывать наш список. Этот код мы вставим в конце функции App.
    return (   <div className="p-10 flex flex-col gap-7 ">     <header className="flex flex-row ">       <div onClick={createItem} className="pt-2 pb-2 pl-3 pr-3 border-2 rounded hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Add new item</div>     </header>     <main className=" flex flex-col flex-auto flex-wrap gap-2">     {/* Place for a list of displaying items  */}     {itemsData ? (itemsData.map((item) => (       <div className="flex flex-row pl-4 h-8 border-2 rounded bg-amber-100">         <div className="flex-grow">           {item.user+'__'+item.file}         </div>         <div className="flex">           <div onClick={()=> editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit</div>           <div onClick={()=> deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete</div>         </div>       </div>     ))) : (<p>Loading...</p>)     }     </main>     {/* Place for modal window */}     <Modal isOpen={isModal} onClose={handleClose} children={formBody}>     </Modal>   </div> );

    Отрисовка модального окна, CRUD

    Как видно из шаблона отрисовки списка, есть такие функции как:
    • createItem
    • editItem
    • deleteItem
    Идея этих функций проста. При нажатии одной из кнопок, к которым данные функции присоединены, будет вставлена соответствующая форма в компонент Modal.
    const deleteItem = (item) => {   handleOpen();   setFormBody(   <div className='flex flex-col justify-between h-full gap-9'>     <b className='flex w-full justify-center text-lg'>       DELETE ITEM     </b>       <div className='flex w-full justify-center'>       Are you sure ?     </div>     <div className='flex w-full justify-center gap-2'>       <button onClick={ () => deletionSubmit(item) } className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Yes</button>       <button onClick={handleClose} className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>No</button>     </div>   </div>) };
    const createItem = () => {   handleOpen();     setFormBody(     <div>       <b className='flex w-full justify-center text-lg'>         ADD NEW ITEM       </b>         <div className='flex w-full justify-around g-4'>         <form className='forma flex flex-col gap-3' onSubmit={creationSubmit} encType="multipart/form-data">             <input type="text" name="username" placeholder="Name" onChange={handleChange}/>             <input type="datetime-local" name="date" placeholder="Time" onChange={handleChange}/>             <input type="file" name="file" placeholder="File" onChange={handleChange}/>             <button className='flex justify-end' type="submit">               <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                 Submit               </div>             </button>         </form>       </div>     </div>   ) };
    const editItem = (item) => {   handleOpen();   setFormBody(     <div>       <b className='flex w-full justify-center text-lg'>         EDIT ITEM       </b>         <div className='flex w-full justify-around g-4'>         <form id={item.id} className='flex flex-col gap-3' onSubmit={editingSubmit}>             <input type="text" name="username" defaultValue={item.user}  onChange={handleChange} />             <input type="datetime-local" name="date" defaultValue={item.time_created}  onChange={handleChange} />             <input type="file" name="file" placeholder='File'  onChange={handleChange}/>             <button className='flex justify-end' type="submit">               <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                 Submit               </div>             </button>         </form>       </div>     </div>) };
    Вставь их выше return.

    Обновление данных форм

    В данных формах за обновление данных отвечает функция handleChange
    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) } }

    Общение с сервером

    За непосредственную отправку запросов на сервер отвечают функции:
    • editingSubmit
    • creationSubmit
    • deletionSubmit
    Они и отвечают за правильное использование axios библиотеки или fetch API
    Функция creationSubmit отправляет POST запросы по адресу /api/results/ . Она сохраняет и отправляет введённые данные и триггерит обновление списка.
    Шеврон Кстати, если бы мы не использовали proxy в package.json, то путь, который мы используем в наших запросах к серверу выглядел бы так: http://localhost:8000/api/results/
    axios
    fetch
    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() };
    Функция editingSubmit отправляет PUT запросы по адресу /api/results/{ID} . Она сохраняет и отправляет введённые данные и триггерит обновление списка.
    axios
    fetch
    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() }
    Функция deletionSubmit отправляет DELETE запросы по адресу /api/results/{ID} . Тем самым удаляя из списка соответствующую запись по ID. Так же в конце триггерит обновление списка.
    axios
    fetch
    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() }

    Обновление списка

    До этого момента я писал о том что функции триггерят обновление списка. В чём дело, а дело в функции useEffect. В моём случае эта функция вызывает handleUpdate, только тогда когда переменная состояния isUpdate (смотри выше) изменена. И меняется она в функциях подтверждения удаления, изменения и добавления записей в базу данных.
    axios
    fetch
    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) };
    Вставь вызов функции useEffect перед самым началом return.
    useEffect(handleUpdate, [isUpdate]);
    Если использовать useEffect без списка зависимостей (т. е. isUpdate в нашем случае ), react будет отправлять запросы об обновлении списка постоянно, что конечно сильно нагружает сервер.
    На этом всё. React приложение готово и оно успешно обменивается запросами с сервером.

    Готовый код для App.js

    axios
    fetch
    import React, { useEffect, useState } from 'react'; import Modal from './Modal'; import axios from "axios"; function App() {   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);   // Is going to close modal window   const handleClose = () => {     setOpen(false);   };     // Is going to open modal window   const handleOpen =() => {     setOpen(true);   };   // Is going to updating formdata   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)     }   }   // Is going to get all data from server   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)   };   // Send a delete request and update list for user   const deletionSubmit = (item) => {     axios.delete(`/api/results/${item.id}/`)       .then((res) => setUpdateTrigger(true));     handleClose()   }   // Paste in modal window delete form   const deleteItem = (item) => {     handleOpen();     setFormBody(     <div className='flex flex-col justify-between h-full gap-9'>       <b className='flex w-full justify-center text-lg'>         DELETE ITEM       </b>         <div className='flex w-full justify-center'>         Are you sure ?       </div>       <div className='flex w-full justify-center gap-2'>         <button onClick={ () => deletionSubmit(item) } className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Yes</button>         <button onClick={handleClose} className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>No</button>       </div>     </div>)   };     // Send a post request with form data to create a new record in database   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()   };   // Paste in modal window create form   const createItem = () => {     handleOpen();         setFormBody(       <div>         <b className='flex w-full justify-center text-lg'>           ADD NEW ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form className='forma flex flex-col gap-3' onSubmit={creationSubmit} encType="multipart/form-data">               <input type="text" name="username" placeholder="Name" onChange={handleChange}/>               <input type="datetime-local" name="date" placeholder="Time" onChange={handleChange}/>               <input type="file" name="file" placeholder="File" onChange={handleChange}/>               <button className='flex justify-end' type="submit">                 <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                   Submit                 </div>               </button>           </form>         </div>       </div>     )   };     // Send a put request with form data to change existing a record in database   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()   }   // Paste in modal window edit form   const editItem = (item) => {     handleOpen();     setFormBody(       <div>         <b className='flex w-full justify-center text-lg'>           EDIT ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form id={item.id} className='flex flex-col gap-3' onSubmit={editingSubmit}>               <input type="text" name="username" defaultValue={item.user}  onChange={handleChange} />               <input type="datetime-local" name="date" defaultValue={item.time_created}  onChange={handleChange} />               <input type="file" name="file" placeholder='File'  onChange={handleChange}/>               <button className='flex justify-end' type="submit">                 <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                   Submit                 </div>               </button>           </form>         </div>       </div>)   };   // Updating list of items when isUpdate is changed   useEffect(handleUpdate, [isUpdate]);   return (     <div className="p-10 flex flex-col gap-7 ">       <header className="flex flex-row ">         <div onClick={createItem} className="pt-2 pb-2 pl-3 pr-3 border-2 rounded hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Add new item</div>       </header>       <main className=" flex flex-col flex-auto flex-wrap gap-2">       {/* Place for a list of displaying items  */}       {itemsData ? (itemsData.map((item) => (         <div className="flex flex-row pl-4 h-8 border-2 rounded bg-amber-100">           <div className="flex-grow">             {item.user+'__'+item.file}           </div>           <div className="flex">             <div onClick={()=> editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit</div>             <div onClick={()=> deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete</div>           </div>         </div>       ))) : (<p>Loading...</p>)       }       </main>       {/* Place for modal window */}       <Modal isOpen={isModal} onClose={handleClose} children={formBody}>       </Modal>     </div>   ); } export default App;
    import React, { useEffect, useState } from 'react'; import Modal from './Modal'; import axios from "axios"; function App() {   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);   // Is going to close modal window   const handleClose = () => {     setOpen(false);   };     // Is going to open modal window   const handleOpen =() => {     setOpen(true);   };   // Is going to updating formdata   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)     }   }   // Is going to get all data from server   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)   };   // Send a delete request and update list for user   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()   }   // Paste in modal window delete form   const deleteItem = (item) => {     handleOpen();     setFormBody(     <div className='flex flex-col justify-between h-full gap-9'>       <b className='flex w-full justify-center text-lg'>         DELETE ITEM       </b>         <div className='flex w-full justify-center'>         Are you sure ?       </div>       <div className='flex w-full justify-center gap-2'>         <button onClick={ () => deletionSubmit(item) } className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>Yes</button>         <button onClick={handleClose} className='bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform'>No</button>       </div>     </div>)   };     // Send a post request with form data to create a new record in database   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()   };   // Paste in modal window create form   const createItem = () => {     handleOpen();         setFormBody(       <div>         <b className='flex w-full justify-center text-lg'>           ADD NEW ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form className='forma flex flex-col gap-3' onSubmit={creationSubmit} encType="multipart/form-data">               <input type="text" name="username" placeholder="Name" onChange={handleChange}/>               <input type="datetime-local" name="date" placeholder="Time" onChange={handleChange}/>               <input type="file" name="file" placeholder="File" onChange={handleChange}/>               <button className='flex justify-end' type="submit">                 <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                   Submit                 </div>               </button>           </form>         </div>       </div>     )   };     // Send a put request with form data to change existing a record in database   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()   }   // Paste in modal window edit form   const editItem = (item) => {     handleOpen();     setFormBody(       <div>         <b className='flex w-full justify-center text-lg'>           EDIT ITEM         </b>           <div className='flex w-full justify-around g-4'>           <form id={item.id} className='flex flex-col gap-3' onSubmit={editingSubmit}>               <input type="text" name="username" defaultValue={item.user}  onChange={handleChange} />               <input type="datetime-local" name="date" defaultValue={item.time_created}  onChange={handleChange} />               <input type="file" name="file" placeholder='File'  onChange={handleChange}/>               <button className='flex justify-end' type="submit">                 <div className='bg-slate-200 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform pl-3 pr-3'>                   Submit                 </div>               </button>           </form>         </div>       </div>)   };   // Updating list of items when isUpdate is changed   useEffect(handleUpdate, [isUpdate]);   return (     <div className="p-10 flex flex-col gap-7 ">       <header className="flex flex-row ">         <div onClick={createItem} className="pt-2 pb-2 pl-3 pr-3 border-2 rounded hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Add new item</div>       </header>       <main className=" flex flex-col flex-auto flex-wrap gap-2">       {/* Place for a list of displaying items  */}       {itemsData ? (itemsData.map((item) => (         <div className="flex flex-row pl-4 h-8 border-2 rounded bg-amber-100">           <div className="flex-grow">             {item.user+'__'+item.file}           </div>           <div className="flex">             <div onClick={()=> editItem(item)} className="bg-slate-200 pl-3 pr-3 hover:bg-slate-50 active:scale-150 hover:cursor-pointer transition-transform">Edit</div>             <div onClick={()=> deleteItem(item)} className="bg-orange-300 pl-3 pr-3 hover:bg-orange-50 active:scale-150 hover:cursor-pointer transition-transform">Delete</div>           </div>         </div>       ))) : (<p>Loading...</p>)       }       </main>       {/* Place for modal window */}       <Modal isOpen={isModal} onClose={handleClose} children={formBody}>       </Modal>     </div>   ); } export default App;

    Заключение

    Конечно, я не рассказал о том как деплоить данный проект на сервер, или то как react будет взаимодействовать с шаблонами от django. Конечно, для них будут отдельные статьи и всё со временем уляжеться. Ну а пока вот так.
    Весь код статьи можно скачать с github, ветка называется simple-integration-react-django https://github.com/DmRafaule/SearchResultParser/tree/simple-integration-react-django

    Не забудь поделиться, лайкнуть и оставить комментарий)

    Комментарии

    (0)

    captcha
    Отправить
    ЗАГРУЗКА ...
    Сейчас тут пусто. Буть первым (o゚v゚)ノ

    Другое

    Похожие статьи


    Создание базового(пустого) React приложения

    Часы
    19.07.2024
    /
    Часы
    05.10.2025
    Глазик
    340
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Туториал по тому, как создать реакт приложение/проекта с использованием npm. С подробным описанием комманд и процессов.

    Как (и можно ли) объединить React JS с Django проектом ч. 1

    Часы
    03.08.2024
    /
    Часы
    02.10.2025
    Глазик
    1388
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    В этой статье ты узнаешь как создать Django приложение и настроить его для работы с React JS чтобы получилось полноценное фулстак приложение. Так же в статье приведено видео-туториал и скачиваемые …

    Новый проект: SearchResultParser | Тим Вебмастер

    Часы
    16.07.2024
    /
    Часы
    05.10.2025
    Глазик
    300
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Буду занят разработкай нового проекта с кодовым названием SearchResultParser. Его суть в том, чтобы парсить данные из поисковой выдачи различных поисковых машин. Таких как google, youtube, yandex и прочих.

    Как построить API для сайта используя Django REST API

    Часы
    24.02.2025
    /
    Часы
    02.10.2025
    Глазик
    4192
    Сердечки
    0
    Соединённые точки
    1
    Соединённые точки
    6
    Соединённые точки
    0
    Здесь ты узнаешь как написать свой API используя Django REST API. Добавляется он для того, чтобы построить API для доступа со стороны клиента (фронтенда). Так же демонстрирую как данный API …

    Использованные термины


    Релевантные вопросы