Разработка на Django и React: создание мощного веб-проекта с нуля
Для кого эта статья:
- Разработчики, заинтересованные в создании веб-приложений с использованием 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):
- Создаем модель в
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
- Создаем сериализатор в
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']
- Определяем представления (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)
- Настраиваем маршрутизацию в
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-приложения:
- Настроим сервис для работы с 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;
- Создадим компоненты для отображения задач (
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;
- Добавим форму для создания задачи (
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;
- Интегрируем компоненты в основной 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:
- Настроим сборку React в директорию, которую Django может обслуживать как статические файлы. Изменим
frontend/package.json:
{
// ... другие настройки
"scripts": {
// ... другие скрипты
"build": "BUILD_PATH='../static/react' react-scripts build"
}
}
- Добавим в 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')],
# ...
},
]
- Создадим Django-представление для обслуживания React-приложения (
views.py):
from django.shortcuts import render
def react_app(request):
return render(request, 'index.html')
- Добавим 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'),
]
- Создадим шаблон
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-аутентификации как наиболее универсального и современного подхода:
- Установка необходимых пакетов в Django:
pip install djangorestframework-simplejwt
- Настройка 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',
}
- Добавление 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'),
]
- Создание пользовательских сериализаторов для аутентификации (опционально):
# 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']
- Создание пользовательских представлений для аутентификации:
# 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)
- Обновление 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:
- Создадим сервис для аутентификации (
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;
- Создадим компоненты для авторизации (
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;
Создадим компонент для регистрации (
frontend/src/components/Register.js) аналогично компоненту Login.Внедрим защиту маршрутов с помощью компонента
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;
- Настроим маршрутизацию в главном компоненте (
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 проекта:
- Подготовка Django-проекта к production
- Сборка React-приложения для production
- Настройка серверной инфраструктуры
- Настройка непрерывной интеграции и доставки (CI/CD)
- Мониторинг и поддержка развернутого приложения
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/)