Шаг 12. Swagger-документация, 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, сигналы, логирование, фильтрация, документация.
- 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 и настройка
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
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. Для уточнения описания можно добавлять декораторы:
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)
🔄 Полный Happy-Path (curl)
Прогоним полный сценарий использования API — от регистрации до создания задачи и проверки уведомлений:
1. Регистрация и логин
# Регистрация
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. Создание проекта
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 -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 -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 -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 -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 -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_BACKEND= smtp (не console) в production -
SMTP-учётные данные в
.env
Логирование и мониторинг
- Логи пишутся в файл или syslog (не только console)
-
Настроен ротационный файловый обработчик (
RotatingFileHandler) - Sentry или аналог для мониторинга 5xx ошибок
API
-
Swagger UI (
/api/docs/) закрыт от публичного доступа в production (или удалён) - Rate limiting настроен (django-ratelimit или через nginx)
Краткая деплой-последовательность (gunicorn + nginx)
# 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/; }
🚀 Что дальше — пути развития
Функциональность
-
Тесты:
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-проект.