Построение 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

Backend на Django с Python - comments

En
Backend python django (python)