Построение backend на Django: от проекта до развёртывания
Введение в бэкенд-разработку с Django
Django - это высокоуровневый веб-фреймворк на Python, который позволяет быстро создавать безопасные и поддерживаемые серверные приложения. В этой части рассматриваются основные подходы к построению backend на Django, начиная с классического MVC (на самом деле MVT) и заканчивая современными архитектурами REST и GraphQL.
Как эффективно реализовать REST API на Django?
Наиболее распространённое и эффективное решение для создания API - использование Django REST Framework (DRF). DRF предоставляет удобные инструменты для сериализации, аутентификации, рендеринга и построения эндпоинтов.
# Установка DRF
pip install djangorestframework
Backend python django (backend на django с python)
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
]
веб библиотека python (веб-библиотека python (фреймворк))
Создание простого API для модели Book:
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_date = models.DateField()
Html python code (встраивание python кода в html)
# serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'author', 'published_date']
Django разработка веб приложений на python (разработка веб-приложений на django)
# views.py
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
Flask на языке python (flask на python)
# urls.py
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Fast api python (fastapi для python)
Типичная ошибка: забыть добавить rest_framework в INSTALLED_APPS или не выполнить миграции после создания модели. В результате Django не найдёт приложение или модель не будет создана в базе данных. Решение - проверить настройки и выполнить python manage.py makemigrations и python manage.py migrate.
Цель использования DRF - быстрое создание полноценного REST API с минимальным кодом, автоматической документацией (при подключении drf-yasg или swagger) и гибкой настройкой прав доступа.
Как создать классическое веб-приложение без REST, используя только встроенные средства Django?
Для традиционных сайтов, которые не требуют отдельного API, можно использовать стандартные контроллеры (Function-Based Views или Class-Based Views) и шаблоны Django.
# views.py
from django.views.generic import ListView
from .models import Book
class BookListView(ListView):
model = Book
template_name = 'books/book_list.html'
context_object_name = 'books'
Python manage py runserver (запуск сервера разработки django через manage.py runserver)
# urls.py
from django.urls import path
from .views import BookListView
urlpatterns = [
path('books/', BookListView.as_view(), name='book-list'),
]
<ul>
{% for book in books %}
<li>{{ book.title }} - {{ book.author }}</li>
{% endfor %}
</ul>
Проблема: если не указать правильный путь к шаблону или забыть создать папку templates внутри приложения, Django выдаст ошибку TemplateDoesNotExist. Решение - проверить структуру папок и настройки TEMPLATES в settings.py.
Этот вариант подходит для небольших проектов, где не требуется отдельный фронтенд-фреймворк и достаточно серверного рендеринга HTML.
Когда стоит использовать GraphQL вместо REST в Django?
Если клиенту требуется гибко запрашивать только нужные поля и связанные данные, удобно использовать Graphene-Django - библиотеку для построения GraphQL API.
# Установка
pip install graphene-django
# settings.py
INSTALLED_APPS = [
...
'graphene_django',
]
GRAPHENE = {
'SCHEMA': 'books.schema.schema'
}
# schema.py
import graphene
from graphene_django.types import DjangoObjectType
from .models import Book
class BookType(DjangoObjectType):
class Meta:
model = Book
class Query(graphene.ObjectType):
all_books = graphene.List(BookType)
def resolve_all_books(self, info):
return Book.objects.all()
schema = graphene.Schema(query=Query)
# urls.py
from django.urls import path
from graphene_django.views import GraphQLView
urlpatterns = [
path('graphql/', GraphQLView.as_view(graphiql=True)),
]
Частая ошибка: не установить библиотеку или не указать правильный путь к схеме в настройках. В результате запросы к GraphQL приводят к Schema not found. Решение - проверить импорт и переменную GRAPHENE['SCHEMA'].
GraphQL оправдан, когда у приложения сложные зависимости данных, несколько типов клиентов (мобильное приложение, SPA) и требуется минимизировать количество запросов.
Как добавить реальное время (WebSockets) в Django-проект?
Для чатов, уведомлений и других real-time функций используется Django Channels. Он заменяет стандартный WSGI на ASGI и позволяет обрабатывать WebSocket-соединения.
# Установка
pip install channels
# settings.py
INSTALLED_APPS = [
...
'channels',
]
ASGI_APPLICATION = 'myproject.asgi.application'
# asgi.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter([
# пути WebSocket
])
),
})
Проблема: при использовании Channels необходимо настроить бэкенд для слоя сообщений (например, Redis), иначе WebSocket-соединения не будут работать между процессами. Типичная ошибка - забыть запустить Redis или указать неверный URL. Решение - установить и запустить Redis, а в настройках указать CHANNEL_LAYERS.
Django Channels применяется в проектах, требующих двусторонней связи: онлайн-чаты, совместное редактирование, ленты уведомлений.
Расширенные примеры кода для Django backend
1. Кастомная валидация сериализатора DRF
Пример проверки уникальности пары полей на уровне сериализатора.
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title', 'author', 'published_date']
def validate(self, data):
# Проверка, что книга с таким названием и автором уже существует
if Book.objects.filter(title=data['title'], author=data['author']).exists():
raise serializers.ValidationError("Книга с таким названием и автором уже существует.")
return data
# Пример запроса POST /api/books/
{
"title": "Война и мир",
"author": "Лев Толстой",
"published_date": "1869-01-01"
}
# Ответ 400
{
"non_field_errors": ["Книга с таким названием и автором уже существует."]
}
2. Аутентификация через JWT с помощью djangorestframework-simplejwt
Настройка получения и обновления токенов.
# Установка
pip install djangorestframework-simplejwt
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
# urls.py
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
# POST /api/token/ с данными {"username": "admin", "password": "123"}
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
3. Использование сигналов для обновления статистики
Автоматическое обновление количества книг у автора при сохранении новой книги.
# models.py
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
class Author(models.Model):
name = models.CharField(max_length=100)
book_count = models.IntegerField(default=0)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
@receiver(post_save, sender=Book)
def update_book_count_on_save(sender, instance, **kwargs):
instance.author.book_count = instance.author.books.count()
instance.author.save()
@receiver(post_delete, sender=Book)
def update_book_count_on_delete(sender, instance, **kwargs):
instance.author.book_count = instance.author.books.count()
instance.author.save()
# После создания книги для автора, поле book_count автоматически увеличится на 1
4. Кастомная команда управления для импорта данных из CSV
Пример команды, которая загружает книги из CSV-файла в базу.
# management/commands/import_books.py
from django.core.management.base import BaseCommand
from myapp.models import Book
import csv
class Command(BaseCommand):
help = 'Импортирует книги из CSV файла'
def add_arguments(self, parser):
parser.add_argument('file_path', type=str, help='Путь к CSV файлу')
def handle(self, *args, **options):
file_path = options['file_path']
with open(file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
Book.objects.create(
title=row['title'],
author=row['author'],
published_date=row['published_date']
)
self.stdout.write(self.style.SUCCESS('Книги успешно импортированы'))
# Вызов команды python manage.py import_books books.csv # Вывод: Книги успешно импортированы
5. Тестирование API с помощью pytest-django
Пример теста для эндпоинта книг.
# tests.py
import pytest
from rest_framework.test import APIClient
from myapp.models import Book
@pytest.mark.django_db
def test_get_books():
Book.objects.create(title='Test', author='Author', published_date='2024-01-01')
client = APIClient()
response = client.get('/api/books/')
assert response.status_code == 200
assert len(response.data) == 1
assert response.data[0]['title'] == 'Test'
# Запуск тестов pytest -v # Вывод: test_get_books PASSED