Разработка на Django и React: создание мощного веб-проекта с нуля

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Разработчики, заинтересованные в создании веб-приложений с использованием Django и React
  • Студенты и начинающие программисты, стремящиеся изучить интеграцию фронтенд и бэкенд технологий
  • Технические лидеры и архитекторы, ищущие лучшие практики для разработки масштабируемых приложений

    Комбинируя мощный бэкенд-фреймворк Django с динамичным фронтенд-инструментом React, разработчики создают высокопроизводительные веб-приложения, способные обрабатывать сложную бизнес-логику и предоставлять впечатляющий пользовательский интерфейс. Разработка проекта на Django с React — это путь к созданию масштабируемых приложений с четким разделением ответственности между фронтендом и бэкендом. Однако интеграция этих двух технологий часто становится камнем преткновения даже для опытных разработчиков. Я проведу вас через весь процесс настройки, разработки и развертывания полнофункционального проекта, используя эти передовые фреймворки. 🚀

Ищете способ быстро освоить разработку проекта на Django с React? Курс Обучение веб-разработке от Skypro даст вам все необходимые инструменты и практические навыки. За 9 месяцев вы пройдете путь от основ до реальных проектов, работая с Django REST Framework и современными фронтенд-библиотеками. Бонусом идут менторская поддержка, код-ревью от практикующих разработчиков и помощь в построении карьеры. Превратите сложную интеграцию фреймворков в свое конкурентное преимущество!

Архитектура проекта Django + React: основные принципы

Прежде чем приступить к написанию кода, необходимо четко понимать архитектурные принципы, лежащие в основе интеграции Django и React. Разработка проекта на Django с React следует паттерну разделения фронтенда и бэкенда, где каждая технология фокусируется на своих сильных сторонах.

Django, как мощный Python-фреймворк, отвечает за:

  • Бизнес-логику и серверную часть приложения
  • Работу с базами данных через ORM
  • Реализацию API через Django REST Framework
  • Аутентификацию и авторизацию пользователей
  • Валидацию данных и бизнес-правил

React, в свою очередь, берет на себя:

  • Пользовательский интерфейс и его интерактивность
  • Управление состоянием приложения на стороне клиента
  • Клиентскую маршрутизацию (с React Router)
  • Потребление API, предоставляемого Django
  • Оптимизацию производительности UI через виртуальный DOM

Существует несколько подходов к интеграции этих фреймворков, и выбор зависит от особенностей вашего проекта:

Подход Описание Преимущества Недостатки
Полное разделение Django и React как отдельные приложения на разных доменах/портах Четкое разделение ответственности, независимое масштабирование Дополнительная настройка CORS, сложности с аутентификацией
Django обслуживает React React как часть Django-проекта, статические файлы через Django Проще настройка, один домен, общие куки Смешивание фронтенда и бэкенда, сложности при масштабировании
Прокси-сервер Nginx/Apache перенаправляет запросы между Django и React Гибкость, производительность, один домен для пользователя Сложность настройки, дополнительная инфраструктура

Михаил Савин, технический архитектор Я столкнулся с выбором архитектуры для крупного корпоративного проекта электронной коммерции. Клиент требовал высокой производительности, масштабируемости и возможности независимых обновлений. После анализа решили использовать полностью разделенную архитектуру: Django REST Framework для API и отдельное React-приложение на другом домене. Это решение полностью оправдало себя, когда через полгода потребовалось добавить мобильное приложение — API уже было готово, нам лишь потребовалось расширить его новыми эндпоинтами. Фронтенд-команда также могла работать в своем темпе, не зависея от бэкенд-разработки. Ключевым фактором успеха стало раннее планирование контрактов API и внедрение автоматических тестов для их валидации.

Выбирая архитектуру, учитывайте размер проекта, команду разработчиков и требования к масштабированию. Для небольших проектов подход "Django обслуживает React" может быть достаточным, но для крупных приложений рекомендуется полное разделение с использованием API. 🏗️

Пошаговый план для смены профессии

Настройка Django REST Framework для работы с React

Фундаментальным шагом в разработке проекта на Django с React является создание и настройка API, через которое фронтенд будет взаимодействовать с бэкендом. Django REST Framework (DRF) — идеальный инструмент для этой задачи, обеспечивающий мощный, гибкий и быстрый способ создания RESTful API.

Начнем с установки необходимых пакетов:

pip install django djangorestframework django-cors-headers

После установки необходимо добавить эти приложения в INSTALLED_APPS в файле settings.py:

INSTALLED_APPS = [
# Django apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

# Third-party apps
'rest_framework',
'corsheaders',

# Local apps
'api',
]

Для обработки кросс-доменных запросов (что необходимо, если React запускается на отдельном сервере), добавьте middleware и настройки CORS:

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # Должен быть перед CommonMiddleware
'django.middleware.common.CommonMiddleware',
# ... остальные middleware
]

# Для разработки можно разрешить все домены
CORS_ALLOW_ALL_ORIGINS = True

# Для продакшена лучше указать конкретные домены
# CORS_ALLOWED_ORIGINS = [
# "https://yourfrontend.com",
# "http://localhost:3000",
# ]

# Разрешить передачу кук в кросс-доменных запросах (если требуется)
CORS_ALLOW_CREDENTIALS = True

Теперь настроим REST Framework для работы с API:

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', # По умолчанию требуем аутентификацию
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication', # Опционально для token-auth
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10, # Пагинация по 10 объектов
}

Для эффективного взаимодействия с React, создадим базовую структуру API. Например, для приложения с задачами (todo-list):

  1. Создаем модель в api/models.py:
from django.db import models
from django.contrib.auth.models import User

class Task(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tasks')

def __str__(self):
return self.title

  1. Создаем сериализатор в api/serializers.py:
from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'completed', 'created_at']
read_only_fields = ['created_at']

  1. Определяем представления (views) в api/views.py:
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Task
from .serializers import TaskSerializer

class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]

def get_queryset(self):
# Возвращаем только задачи текущего пользователя
return Task.objects.filter(user=self.request.user).order_by('-created_at')

def perform_create(self, serializer):
# Автоматически привязываем задачу к текущему пользователю
serializer.save(user=self.request.user)

  1. Настраиваем маршрутизацию в api/urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import TaskViewSet

router = DefaultRouter()
router.register('tasks', TaskViewSet, basename='task')

urlpatterns = [
path('', include(router.urls)),
]

И обновляем основной файл URL в project/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
path('api-auth/', include('rest_framework.urls')), # Для browsable API
]

Теперь у нас есть полнофункциональное API с эндпоинтами для CRUD-операций с задачами:

  • GET /api/tasks/ — получение списка задач
  • POST /api/tasks/ — создание новой задачи
  • GET /api/tasks/{id}/ — получение деталей задачи
  • PUT/PATCH /api/tasks/{id}/ — обновление задачи
  • DELETE /api/tasks/{id}/ — удаление задачи

Рекомендации по оптимизации API для работы с React:

Аспект API Рекомендация
Структура ответов Старайтесь делать структуру JSON плоской и предсказуемой, это упрощает работу с данными в React
Пагинация Всегда используйте пагинацию для коллекций, даже если данных пока немного
Фильтрация Реализуйте фильтрацию на стороне сервера (django-filter), а не в React
Вложенные ресурсы Используйте вложенные сериализаторы для связанных данных, но избегайте глубокой вложенности
Версионирование С самого начала предусмотрите версионирование API (/api/v1/)

Тщательное проектирование API на этапе настройки Django REST Framework значительно упростит разработку React-приложения и интеграцию фронтенда с бэкендом. 🔌

Создание React-приложения в проекте на Django

После настройки API бэкенда, следующим шагом в разработке проекта на Django с React является создание и интеграция React-приложения. Существует несколько подходов, но я рассмотрю наиболее гибкий и масштабируемый вариант, где React-приложение создается отдельно, а затем интегрируется с Django.

Для начала, создадим новое React-приложение с помощью Create React App (CRA). Убедитесь, что у вас установлен Node.js и npm, затем выполните:

# Создаем приложение в корневой директории Django проекта
npx create-react-app frontend

# Переходим в директорию React приложения
cd frontend

После создания базового приложения, установим необходимые зависимости для работы с API:

# Установка Axios для HTTP-запросов
npm install axios

# Установка React Router для клиентской маршрутизации
npm install react-router-dom

# Опционально: установка библиотеки управления состоянием
npm install @reduxjs/toolkit react-redux

Теперь настроим прокси для разработки, чтобы React мог взаимодействовать с Django API без проблем с CORS. Добавьте следующую строку в frontend/package.json:

{
"name": "frontend",
// ... другие настройки
"proxy": "http://localhost:8000"
}

Это позволит во время разработки отправлять запросы к /api/... без указания полного URL.

Создадим базовую структуру React-приложения:

  1. Настроим сервис для работы с API (frontend/src/services/api.js):
import axios from 'axios';

// Создаем экземпляр axios с базовым URL
const api = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json',
},
withCredentials: true, // Для передачи кук при кросс-доменных запросах
});

// Добавляем перехватчик для обработки ошибок
api.interceptors.response.use(
response => response,
error => {
// Обработка ошибок авторизации и т.д.
if (error.response && error.response.status === 401) {
// Перенаправление на страницу логина или другая логика
window.location = '/login';
}
return Promise.reject(error);
}
);

// Методы API
export const TaskAPI = {
// Получение списка задач
getTasks: () => api.get('/tasks/'),

// Получение одной задачи
getTask: id => api.get(`/tasks/${id}/`),

// Создание задачи
createTask: taskData => api.post('/tasks/', taskData),

// Обновление задачи
updateTask: (id, taskData) => api.put(`/tasks/${id}/`, taskData),

// Удаление задачи
deleteTask: id => api.delete(`/tasks/${id}/`),
};

export default api;

  1. Создадим компоненты для отображения задач (frontend/src/components/TaskList.js):
import React, { useEffect, useState } from 'react';
import { TaskAPI } from '../services/api';

const TaskList = () => {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchTasks = async () => {
try {
setLoading(true);
const response = await TaskAPI.getTasks();
setTasks(response.data.results || response.data);
setError(null);
} catch (err) {
setError('Не удалось загрузить задачи');
console.error(err);
} finally {
setLoading(false);
}
};

fetchTasks();
}, []);

if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error}</p>;

return (
<div className="task-list">
<h2>Мои задачи</h2>
{tasks.length === 0 ? (
<p>Задач нет</p>
) : (
<ul>
{tasks.map(task => (
<li key={task.id}>
<h3>{task.title}</h3>
<p>{task.description}</p>
<p>Статус: {task.completed ? 'Выполнено' : 'В процессе'}</p>
</li>
))}
</ul>
)}
</div>
);
};

export default TaskList;

  1. Добавим форму для создания задачи (frontend/src/components/TaskForm.js):
import React, { useState } from 'react';
import { TaskAPI } from '../services/api';

const TaskForm = ({ onTaskAdded }) => {
const [formData, setFormData] = useState({
title: '',
description: '',
completed: false,
});
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);

const handleChange = e => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
};

const handleSubmit = async e => {
e.preventDefault();

try {
setSubmitting(true);
setError(null);

// Отправляем данные на сервер
const response = await TaskAPI.createTask(formData);

// Сбрасываем форму
setFormData({
title: '',
description: '',
completed: false,
});

// Уведомляем родительский компонент о создании задачи
if (onTaskAdded) onTaskAdded(response.data);

} catch (err) {
setError('Не удалось создать задачу');
console.error(err);
} finally {
setSubmitting(false);
}
};

return (
<div className="task-form">
<h2>Создать новую задачу</h2>
{error && <p className="error">{error}</p>}

<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="title">Название:</label>
<input
type="text"
id="title"
name="title"
value={formData.title}
onChange={handleChange}
required
/>
</div>

<div className="form-group">
<label htmlFor="description">Описание:</label>
<textarea
id="description"
name="description"
value={formData.description}
onChange={handleChange}
/>
</div>

<div className="form-group">
<label>
<input
type="checkbox"
name="completed"
checked={formData.completed}
onChange={handleChange}
/>
Выполнено
</label>
</div>

<button type="submit" disabled={submitting}>
{submitting ? 'Создание...' : 'Создать задачу'}
</button>
</form>
</div>
);
};

export default TaskForm;

  1. Интегрируем компоненты в основной App (frontend/src/App.js):
import React, { useState, useEffect } from 'react';
import TaskList from './components/TaskList';
import TaskForm from './components/TaskForm';
import './App.css';

function App() {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [refreshTasks, setRefreshTasks] = useState(false);

const handleTaskAdded = (newTask) => {
setTasks(prevTasks => [newTask, ...prevTasks]);
setRefreshTasks(!refreshTasks);
};

return (
<div className="App">
<header className="App-header">
<h1>Управление задачами</h1>
</header>

<main>
<TaskForm onTaskAdded={handleTaskAdded} />
<TaskList key={refreshTasks} />
</main>
</div>
);
}

export default App;

Для интеграции React с Django нам нужно настроить сборку React-приложения и его обслуживание через Django:

  1. Настроим сборку React в директорию, которую Django может обслуживать как статические файлы. Изменим frontend/package.json:
{
// ... другие настройки
"scripts": {
// ... другие скрипты
"build": "BUILD_PATH='../static/react' react-scripts build"
}
}

  1. Добавим в Django настройки для обслуживания React (settings.py):
# Директории со статическими файлами
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

# Шаблоны
TEMPLATES = [
{
# ... другие настройки
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# ...
},
]

  1. Создадим Django-представление для обслуживания React-приложения (views.py):
from django.shortcuts import render

def react_app(request):
return render(request, 'index.html')

  1. Добавим URL-маршрут для React (urls.py):
from django.urls import path, re_path
from django.views.generic import TemplateView
from . import views

urlpatterns = [
# ... другие URL-маршруты

# Этот маршрут обрабатывает все URL-адреса, которые должны обслуживаться React Router
re_path(r'^.*$', views.react_app, name='react-app'),
]

  1. Создадим шаблон templates/index.html для React:
{% load static %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django React App</title>
<link rel="stylesheet" href="{% static 'react/css/main.css' %}">
</head>
<body>
<div id="root">
<!-- React будет монтироваться сюда -->
</div>

<!-- Подключаем сгенерированные скрипты React -->
<script src="{% static 'react/js/main.js' %}"></script>
</body>
</html>

Анна Козлова, frontend-разработчик Мне поручили перевести монолитный Django-проект на React фронтенд, сохранив весь функционал. Изначально я попыталась внедрять React постепенно, компонент за компонентом, но это создало множество проблем с несогласованным состоянием UI и сложностями с маршрутизацией. Тогда я изменила подход: создала полноценное React SPA, которое полностью заменило шаблоны Django. Ключевым моментом стала разработка правильной структуры для взаимодействия с API — я создала специальные хуки и сервисы, которые инкапсулировали всю логику работы с бэкендом. Это решение позволило нам разделить команду на фронтенд и бэкенд разработчиков, которые могли работать параллельно. Мы также смогли внедрить современные паттерны разработки на фронтенде, такие как атомарный дизайн и управление состоянием через Redux Toolkit, что сделало код более поддерживаемым.

После настройки вы можете разрабатывать React-приложение независимо с помощью npm start (запускает на порту 3000) и взаимодействовать с Django API через прокси. Для продакшена выполните npm run build, чтобы собрать React-приложение, которое Django будет обслуживать как статические файлы. 🎨

Интеграция аутентификации между Django и React

Аутентификация — одна из самых сложных частей в разработке проекта на Django с React из-за необходимости безопасного обмена учетными данными между разделенными фронтендом и бэкендом. Правильно реализованная аутентификация обеспечивает безопасность приложения и удобство для пользователей.

Существует несколько подходов к аутентификации в приложении Django + React:

Метод аутентификации Описание Плюсы Минусы Сложность интеграции
Сессионная аутентификация Использование куки и сессий Django Встроенная в Django, простая в использовании Требует настройки CSRF, не подходит для мобильных приложений Средняя
Token-аутентификация Использование простых токенов Встроена в DRF, простая реализация Токены не истекают автоматически, нет refresh-токенов Низкая
JWT-аутентификация Использование JSON Web Tokens Stateless, хорошо масштабируется, поддержка refresh-токенов Сложнее в реализации, требует дополнительных библиотек Высокая
OAuth2 Полноценный протокол авторизации Очень гибкий, поддержка внешних провайдеров Наиболее сложный в реализации, избыточен для простых проектов Очень высокая

Рассмотрим реализацию JWT-аутентификации как наиболее универсального и современного подхода:

  1. Установка необходимых пакетов в Django:
pip install djangorestframework-simplejwt

  1. Настройка JWT в settings.py:
INSTALLED_APPS = [
# ... другие приложения
'rest_framework',
'corsheaders',
'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}

# Настройки JWT
from datetime import timedelta

SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}

  1. Добавление URL-маршрутов для JWT в urls.py:
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView,
)

urlpatterns = [
# ... другие URL-маршруты
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

  1. Создание пользовательских сериализаторов для аутентификации (опционально):
# serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)

# Добавляем пользовательские данные в токен
token['username'] = user.username
token['email'] = user.email

return token

class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']

  1. Создание пользовательских представлений для аутентификации:
# views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework.permissions import AllowAny, IsAuthenticated
from .serializers import MyTokenObtainPairSerializer, UserSerializer

class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer

class UserRegistrationView(APIView):
permission_classes = [AllowAny]

def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
user.set_password(request.data.get('password'))
user.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class UserProfileView(APIView):
permission_classes = [IsAuthenticated]

def get(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)

def put(self, request):
serializer = UserSerializer(request.user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

  1. Обновление URL-маршрутов с новыми представлениями:
from .views import MyTokenObtainPairView, UserRegistrationView, UserProfileView

urlpatterns = [
# ... другие URL-маршруты
path('api/token/', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/register/', UserRegistrationView.as_view(), name='register'),
path('api/profile/', UserProfileView.as_view(), name='profile'),
]

Теперь перейдем к интеграции JWT-аутентификации в React:

  1. Создадим сервис для аутентификации (frontend/src/services/auth.js):
import api from './api';

// Время жизни токена в миллисекундах
const ACCESS_TOKEN_LIFETIME = 15 * 60 * 1000; // 15 минут

export const AuthService = {
// Авторизация пользователя
login: async (username, password) => {
try {
const response = await api.post('/token/', { username, password });
const { access, refresh } = response.data;

// Сохраняем токены в localStorage
localStorage.setItem('access_token', access);
localStorage.setItem('refresh_token', refresh);
localStorage.setItem('token_expiry', Date.now() + ACCESS_TOKEN_LIFETIME);

// Устанавливаем токен в заголовки запросов
api.defaults.headers.common['Authorization'] = `Bearer ${access}`;

return response.data;
} catch (error) {
throw error;
}
},

// Регистрация нового пользователя
register: async (userData) => {
try {
const response = await api.post('/register/', userData);
return response.data;
} catch (error) {
throw error;
}
},

// Выход пользователя
logout: () => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('token_expiry');
delete api.defaults.headers.common['Authorization'];
},

// Получение данных профиля
getProfile: async () => {
try {
const response = await api.get('/profile/');
return response.data;
} catch (error) {
throw error;
}
},

// Обновление токена
refreshToken: async () => {
try {
const refresh = localStorage.getItem('refresh_token');
if (!refresh) throw new Error('Refresh token not found');

const response = await api.post('/token/refresh/', { refresh });
const { access } = response.data;

localStorage.setItem('access_token', access);
localStorage.setItem('token_expiry', Date.now() + ACCESS_TOKEN_LIFETIME);
api.defaults.headers.common['Authorization'] = `Bearer ${access}`;

return access;
} catch (error) {
AuthService.logout();
throw error;
}
},

// Проверка и обновление токена при необходимости
checkAndRefreshToken: async () => {
const expiry = localStorage.getItem('token_expiry');
const now = Date.now();

// Если токен истекает в ближайшие 5 минут, обновляем его
if (expiry && now > expiry – 5 * 60 * 1000) {
return AuthService.refreshToken();
}

const token = localStorage.getItem('access_token');
if (token) {
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}

return token;
},

// Проверка авторизации пользователя
isAuthenticated: () => {
return !!localStorage.getItem('access_token');
}
};

// Инициализация: устанавливаем токен при загрузке страницы
AuthService.checkAndRefreshToken();

// Добавляем перехватчик для автоматического обновления токена перед запросами
api.interceptors.request.use(
async (config) => {
await AuthService.checkAndRefreshToken();
return config;
},
(error) => Promise.reject(error)
);

export default AuthService;

  1. Создадим компоненты для авторизации (frontend/src/components/Login.js):
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthService from '../services/auth';

const Login = ({ onLogin }) => {
const [credentials, setCredentials] = useState({ username: '', password: '' });
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const navigate = useNavigate();

const handleChange = (e) => {
const { name, value } = e.target;
setCredentials(prev => ({ ...prev, [name]: value }));
};

const handleSubmit = async (e) => {
e.preventDefault();
setError('');
setLoading(true);

try {
await AuthService.login(credentials.username, credentials.password);
setLoading(false);

if (onLogin) onLogin();
navigate('/dashboard');
} catch (err) {
setLoading(false);
setError(
err.response?.data?.detail || 
'Произошла ошибка при авторизации. Проверьте имя пользователя и пароль.'
);
}
};

return (
<div className="login-form">
<h2>Вход в систему</h2>
{error && <div className="error-message">{error}</div>}

<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="username">Имя пользователя:</label>
<input
type="text"
id="username"
name="username"
value={credentials.username}
onChange={handleChange}
required
/>
</div>

<div className="form-group">
<label htmlFor="password">Пароль:</label>
<input
type="password"
id="password"
name="password"
value={credentials.password}
onChange={handleChange}
required
/>
</div>

<button type="submit" disabled={loading}>
{loading ? 'Выполняется вход...' : 'Войти'}
</button>
</form>
</div>
);
};

export default Login;

  1. Создадим компонент для регистрации (frontend/src/components/Register.js) аналогично компоненту Login.

  2. Внедрим защиту маршрутов с помощью компонента PrivateRoute (frontend/src/components/PrivateRoute.js):

import React from 'react';
import { Navigate } from 'react-router-dom';
import AuthService from '../services/auth';

const PrivateRoute = ({ children }) => {
const isAuthenticated = AuthService.isAuthenticated();

if (!isAuthenticated) {
// Перенаправляем на страницу логина, если пользователь не авторизован
return <Navigate to="/login" replace />;
}

return children;
};

export default PrivateRoute;

  1. Настроим маршрутизацию в главном компоненте (frontend/src/App.js):
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import Login from './components/Login';
import Register from './components/Register';
import Dashboard from './components/Dashboard';
import PrivateRoute from './components/PrivateRoute';
import AuthService from './services/auth';
import './App.css';

function App() {
const [isAuthenticated, setIsAuthenticated] = useState(AuthService.isAuthenticated());

const handleLogin = () => {
setIsAuthenticated(true);
};

const handleLogout = () => {
AuthService.logout();
setIsAuthenticated(false);
};

return (
<Router>
<div className="App">
<Routes>
<Route path="/login" element={
isAuthenticated ? <Navigate to="/dashboard" /> : <Login onLogin={handleLogin} />
} />

<Route path="/register" element={
isAuthenticated ? <Navigate to="/dashboard" /> : <Register />
} />

<Route path="/dashboard" element={
<PrivateRoute>
<Dashboard onLogout={handleLogout} />
</PrivateRoute>
} />

<Route path="/" element={<Navigate to="/dashboard" />} />
</Routes>
</div>
</Router>
);
}

export default App;

Эта реализация обеспечивает безопасную аутентификацию пользователей между Django и React с использованием JWT. Она включает управление токенами, их автоматическое обновление и защиту маршрутов. 🔐

Развертывание Django-React проекта в production

После завершения разработки проекта на Django с React, следующим ключевым шагом является правильное развертывание приложения в production-среде. Этот процесс требует внимательного отношения к безопасности, производительности и масштабируемости вашего приложения.

Рассмотрим основные этапы и стратегии развертывания Django-React проекта:

  1. Подготовка Django-проекта к production
  2. Сборка React-приложения для production
  3. Настройка серверной инфраструктуры
  4. Настройка непрерывной интеграции и доставки (CI/CD)
  5. Мониторинг и поддержка развернутого приложения

1. Подготовка Django-проекта к production

Прежде всего, необходимо оптимизировать настройки Django для production-среды:

# settings.py

# Безопасность
DEBUG = False
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
ALLOWED_HOSTS = ['example.com', 'www.example.com']

# HTTPS настройки
SECURE_HSTS_SECONDS = 31536000 # 1 год
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_PRELOAD = True

# Статические и медиа файлы
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

# База данных
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}

# Кеширование
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': os.environ.get('REDIS_URL', 'redis://localhost:6379/1'),
}
}

# Настройка логирования
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'WARNING',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_DIR, 'logs/django.log'),
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
}

**Читайте также**
- [Python для веб-разработки: возможности, фреймворки, практики](/python/razrabotka-veb-prilozhenij-na-python/)
- [Python-скрипты: автоматизация рутинных задач в несколько строк кода](/python/kak-pisat-i-ispolzovat-skripty-na-python/)
- [Как начать создавать веб-сайты на Python без опыта кодирования](/python/vvedenie-v-veb-razrabotku-na-python/)
- [Как создать iOS-приложения на Python: пошаговое руководство](/python/python-dlya-ios-kak-nachat-razrabotku/)
- [Full-stack разработчик на Python: от новичка до профессионала](/python/full-stack-razrabotchik-na-python-chto-nuzhno-znat/)
- [Программирование игр на Python: от основ к мастерству разработки](/python/izuchenie-python-cherez-razrabotku-igr/)
- [Оптимальная фильтрация данных в Django ORM: секреты экспертов](/python/metody-filtracii-v-django/)
- [Python для веб-разработки: самые востребованные навыки и фреймворки](/python/vakansii-programmist-python-dlya-web-prilozhenij/)
- [6 методов проверки и улучшения Python-кода для разработчиков](/python/kak-proverit-i-uluchshit-kod-na-python/)
- [VS Code для Python: настройка редактора для эффективной разработки](/python/nastrojka-vs-code-dlya-razrabotki-na-python/)

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Какой фреймворк используется для создания серверной логики в проекте?
1 / 5

Загрузка...