Шаг 12. Swagger-документация, happy-path и деплой

📁 Серия: Капстоун C ⏱️ ~60 мин 🎯 Сложность: Средняя
#drf-spectacular #swagger-ui #openapi #happy-path #финал

⚡ Кратко: что делаем на этом шаге

Цель: Подключить drf-spectacular для автогенерации OpenAPI 3 схемы и Swagger UI. Прогнать полный happy-path (регистрация → логин → CRUD → права → email). Проверить production-чеклист.

  • Установка: pip install drf-spectacular
  • URL: /api/schema/ (JSON), /api/docs/ (Swagger UI), /api/redoc/ (ReDoc)
  • Результат: рабочий API с документацией — готово к демонстрации

🎯 Цель этапа

Финальный шаг. Добавляем автоматическую документацию через drf-spectacular — оно читает DRF ViewSets, Serializers и генерирует OpenAPI 3 схему. Swagger UI и ReDoc рендерят эту схему в удобный интерактивный интерфейс.

После этого шага у нас есть production-grade REST API: JWT-аутентификация, object-level permissions, сигналы, логирование, фильтрация, документация.

🎉 Что мы построили за 12 шагов:
  • REST API для управления проектами и задачами
  • Кастомная User-модель с email как логином
  • Три сущности (Project, Task, Comment) с полным CRUD
  • Soft-delete для задач (данные не теряются)
  • Фильтрация, поиск, сортировка, пагинация
  • JWT-аутентификация (access + refresh ротация)
  • Object-level permissions (IsProjectOwner, IsProjectMember)
  • Email-уведомления через Django signals
  • Структурированное логирование, кастомный exception handler
  • OpenAPI 3 документация с Swagger UI

📄 Затрагиваемые файлы

ФайлДействиеОписание
config/settings.pyОбновитьSPECTACULAR_SETTINGS, django_spectacular в INSTALLED_APPS
config/urls.pyОбновитьЭндпоинты /api/schema/, /api/docs/, /api/redoc/

🔨 Шаги

1. Установка drf-spectacular

💻 Терминал
pip install drf-spectacular
pip freeze | grep spectacular >> requirements.txt

2. Регистрация в INSTALLED_APPS и настройка

📄 config/settings.py
INSTALLED_APPS = [
    # ...
    "rest_framework",
    "rest_framework_simplejwt",
    "django_filters",
    "drf_spectacular",     # ← добавить
    "apps.users",
    "apps.projects",
    "apps.tasks",
]

REST_FRAMEWORK = {
    # ... существующие настройки ...
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",  # ← добавить
}

SPECTACULAR_SETTINGS = {
    "TITLE": "Task Manager API",
    "DESCRIPTION": (
        "REST API для управления проектами и задачами. "
        "Аутентификация через JWT (Bearer token). "
        "Разработано в рамках Python Advanced — Капстоун C."
    ),
    "VERSION": "1.0.0",
    "SERVE_INCLUDE_SCHEMA": False,  # не включать схему в саму схему
    # Документируем JWT-заголовок для Swagger UI
    "SECURITY": [{"BearerAuth": []}],
    "COMPONENTS": {
        "securitySchemes": {
            "BearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT",
            }
        }
    },
}

3. Подключение URL-эндпоинтов документации

📄 config/urls.py
# config/urls.py
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularSwaggerView,
    SpectacularRedocView,
)

urlpatterns = [
    path("admin/", admin.site.urls),

    # Auth: регистрация, логин, refresh, me
    path("api/auth/", include("apps.users.urls")),

    # API ресурсов
    path("api/", include("apps.projects.urls")),
    path("api/", include("apps.tasks.urls")),

    # Документация API
    # GET /api/schema/ → OpenAPI 3 JSON/YAML схема
    path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
    # GET /api/docs/ → Swagger UI (интерактивная документация)
    path("api/docs/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
    # GET /api/redoc/ → ReDoc (читаемая документация)
    path("api/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]

4. Аннотации @extend_schema (опционально)

drf-spectacular автоматически генерирует схему из ViewSet/Serializer. Для уточнения описания можно добавлять декораторы:

📄 apps/tasks/views.py (пример)
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes


class TaskViewSet(viewsets.ModelViewSet):
    """CRUD для задач с фильтрацией, поиском, пагинацией."""

    @extend_schema(
        summary="Список задач",
        description="Возвращает страничный список задач с поддержкой фильтрации и поиска.",
        parameters=[
            OpenApiParameter("status", OpenApiTypes.STR, description="Фильтр по статусу"),
            OpenApiParameter("priority", OpenApiTypes.STR, description="Фильтр по приоритету"),
            OpenApiParameter("search", OpenApiTypes.STR, description="Поиск по title и description"),
            OpenApiParameter("ordering", OpenApiTypes.STR, description="Сортировка: created_at, -created_at"),
        ],
        responses={200: TaskListSerializer(many=True)},
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
💡 Когда нужен @extend_schema? Только когда автогенерация неточна: нестандартные ответы, custom actions, сложные параметры. Для стандартных CRUD drf-spectacular справляется сам.

🔄 Полный Happy-Path (curl)

Прогоним полный сценарий использования API — от регистрации до создания задачи и проверки уведомлений:

1. Регистрация и логин

💻 PowerShell / bash
# Регистрация
curl -s -X POST http://127.0.0.1:8000/api/auth/register/ \
  -H "Content-Type: application/json" \
  -d '{"email":"alice@example.com","username":"alice","password":"Alice123!","password_confirm":"Alice123!"}' \
  | python -m json.tool

# Логин — получаем токены
TOKENS=$(curl -s -X POST http://127.0.0.1:8000/api/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{"email":"alice@example.com","password":"Alice123!"}')
ACCESS=$(echo $TOKENS | python -c "import sys,json; print(json.load(sys.stdin)['access'])")
REFRESH=$(echo $TOKENS | python -c "import sys,json; print(json.load(sys.stdin)['refresh'])")
echo "Access token получен"

2. Создание проекта

💻 curl
PROJECT=$(curl -s -X POST http://127.0.0.1:8000/api/projects/ \
  -H "Authorization: Bearer $ACCESS" \
  -H "Content-Type: application/json" \
  -d '{"name":"Мой первый проект","description":"Тест API"}')
PROJECT_ID=$(echo $PROJECT | python -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Проект создан, ID=$PROJECT_ID"

3. Создание задачи с assignee

💻 curl
curl -s -X POST http://127.0.0.1:8000/api/tasks/ \
  -H "Authorization: Bearer $ACCESS" \
  -H "Content-Type: application/json" \
  -d "{\"title\":\"Реализовать авторизацию\",\"project\":$PROJECT_ID,\"assignee\":1,\"priority\":\"high\"}" \
  | python -m json.tool
# В консоли runserver должно появиться email-уведомление

4. Фильтрация задач

💻 curl
curl -s "http://127.0.0.1:8000/api/tasks/?status=todo&priority=high&ordering=-created_at" \
  -H "Authorization: Bearer $ACCESS" \
  | python -m json.tool

5. Обновление статуса задачи

💻 curl
curl -s -X PATCH http://127.0.0.1:8000/api/tasks/1/ \
  -H "Authorization: Bearer $ACCESS" \
  -H "Content-Type: application/json" \
  -d '{"status":"in_progress"}' \
  | python -m json.tool

6. Soft-delete задачи

💻 curl
curl -s -X DELETE http://127.0.0.1:8000/api/tasks/1/ \
  -H "Authorization: Bearer $ACCESS" \
  -w "\nHTTP: %{http_code}\n"
# 204 No Content — задача помечена удалённой

curl -s http://127.0.0.1:8000/api/tasks/1/ \
  -H "Authorization: Bearer $ACCESS" \
  -w "\nHTTP: %{http_code}\n"
# 404 Not Found — из API не видна

7. Обновление access-токена

💻 curl
curl -s -X POST http://127.0.0.1:8000/api/auth/token/refresh/ \
  -H "Content-Type: application/json" \
  -d "{\"refresh\":\"$REFRESH\"}" \
  | python -m json.tool

✅ Production-чеклист

Перед деплоем убедитесь, что следующие пункты выполнены:

Безопасность

  • DEBUG = False в production
  • SECRET_KEY — случайная строка из 50+ символов, хранится в .env, не в репозитории
  • ALLOWED_HOSTS — только ваши домены, не ["*"]
  • JWT ACCESS_TOKEN_LIFETIME ≤ 15 минут
  • В production: httpOnly-куки для токенов (не localStorage)
  • CORS_ALLOWED_ORIGINS ограничен списком доверенных доменов (если есть фронтенд)

База данных

  • Переключиться с SQLite на PostgreSQL
  • Учётные данные БД — в .env, не захардкожены
  • Все миграции применены: python manage.py migrate
  • Индексы созданы (они в Meta.indexes моделей — применяются при migrate)

Статика и медиа

  • python manage.py collectstatic выполнен
  • Статика раздаётся через nginx/CDN, не Django

Email

  • EMAIL_BACKEND = smtp (не console) в production
  • SMTP-учётные данные в .env

Логирование и мониторинг

  • Логи пишутся в файл или syslog (не только console)
  • Настроен ротационный файловый обработчик (RotatingFileHandler)
  • Sentry или аналог для мониторинга 5xx ошибок

API

  • Swagger UI (/api/docs/) закрыт от публичного доступа в production (или удалён)
  • Rate limiting настроен (django-ratelimit или через nginx)

Краткая деплой-последовательность (gunicorn + nginx)

💻 Сервер (Ubuntu/Debian)
# 1. Установка зависимостей
pip install -r requirements.txt
pip install gunicorn

# 2. Переменные окружения
cp .env.example .env
# Редактируем .env: DEBUG=False, SECRET_KEY=..., DATABASE_URL=...

# 3. Миграции и статика
python manage.py migrate
python manage.py collectstatic --no-input
python manage.py createsuperuser

# 4. Запуск gunicorn
gunicorn config.wsgi:application \
  --bind 0.0.0.0:8000 \
  --workers 3 \
  --timeout 60 \
  --access-logfile - \
  --error-logfile -

# 5. nginx проксирует на gunicorn (конфиг /etc/nginx/sites-available/taskmanager):
#    location /api/ { proxy_pass http://127.0.0.1:8000; }
#    location /static/ { alias /path/to/staticfiles/; }
⚠️ Проверить по документации: Конкретные настройки gunicorn (число воркеров, таймауты) зависят от характеристик вашего сервера. Правило: workers = 2 * CPU + 1. Проверьте актуальные рекомендации в документации gunicorn.

🚀 Что дальше — пути развития

Функциональность

  • Тесты: rest_framework.test.APITestCase — написать тесты для всех эндпоинтов. Начать с «золотых путей» (happy path), затем негативные сценарии.
  • Celery + Redis: вынести отправку email в асинхронную очередь. Сигнал кладёт задачу в очередь → Celery-воркер отправляет email.
  • WebSocket-уведомления: Django Channels для real-time обновлений статуса задач — замена polling-а.
  • Версионирование API: добавить /api/v1/ и /api/v2/ для обратной совместимости при изменении контракта.

Infrastructure

  • Docker: Dockerfile + docker-compose.yml для воспроизводимого окружения (Django + PostgreSQL + Redis).
  • CI/CD: GitHub Actions — автоматический запуск тестов при push, деплой при merge в main.
  • Кеширование: Redis + django-redis для кеширования тяжёлых запросов (список проектов, dashboard-статистика).

🎓 Поздравляем!

Вы прошли флагманский капстоун Python Advanced — 12 шагов от пустой папки до production-grade REST API. Вы применили на практике:

  • Django 5 ORM, модели, Admin, миграции
  • DRF: Serializers, ViewSets, Routers, Pagination, Filters
  • JWT-аутентификация (SimpleJWT) с refresh-ротацией
  • Object-level permissions и кастомные разрешения
  • Django signals и email-уведомления
  • Структурированное логирование и обработка ошибок
  • OpenAPI документация через drf-spectacular

Следующий шаг: добавить тесты (APITestCase) и Docker-обёртку — это превратит учебный проект в полноценный portfolio-проект.