
Я создаю веб-приложения на Python уже четыре года. Первые два года моим стандартом был Django. Последние два года в большинстве проектов его заменил FastAPI. Но я никогда не делал полноценного сравнения всех основных вариантов сразу.
Поэтому я реализовал один и тот же API на Django, FastAPI, Flask и Starlette. Одни и те же эндпоинты, одна и та же база данных, одна и та же бизнес-логика. Я измерил производительность под нагрузкой, скорость разработки, сложность кода и то, как каждый фреймворк справляется с продакшен-требованиями.
Один результат стал для меня полной неожиданностью. Представляю вашему вниманию его честный разбор.
Тест
В API было пять эндпоинтов: регистрация пользователя, вход с генерацией JWT, пагинированный список с обращением к PostgreSQL, детальный эндпоинт со связанными данными и эндпоинт с фоновой задачей, запускающей асинхронную работу.
Это все покрывает паттерны, которые встречаются почти в каждом реальном приложении: аутентификация, доступ к базе данных, пагинация, связанные данные и асинхронные операции.
Каждый фреймворк я запускал на одинаковом оборудовании с одинаковой конфигурацией базы данных. Нагрузочное тестирование проводилось с 100 одновременными пользователями в течение 5 минут.
Django
Django — самый полноценный Python-фреймворк из доступных. Он сразу поставляется с ORM, админ-панелью, аутентификацией, работой с формами и системой миграций.
Скорость разработки: Django позволил стартовать быстрее всего. Структура проекта прописана, инструменты для создания каркаса берут на себя большую часть шаблонного кода. Эндпоинты регистрации и входа заработали менее чем за 30 минут благодаря встроенной аутентификации Django.
Производительность под нагрузкой:
- Среднее время ответа: 187 мс.
- P95: 312 мс.
- Запросов в секунду: 284.
- Доля ошибок: 0,2%.
Добротно, но не исключительно. Синхронная архитектура Django означает, что каждый воркер обрабатывает один запрос за раз. При параллельной нагрузке запросы выстраиваются в очередь.
Сложность кода: Эндпоинт пагинированного списка со связанными данными получился чище всех четырех фреймворков. ORM Django элегантно обрабатывает связанные данные.
# Django - чистота и читаемость
def order_list(request):
orders = Order.objects.select_related("user").prefetch_related("items")
paginator = Paginator(orders, 20)
page = paginator.get_page(request.GET.get("page", 1))
return JsonResponse({"orders": list(page.object_list.values())})
Где возникли сложности: Эндпоинт с фоновой задачей потребовал Celery для корректной обработки асинхронных заданий. Это дополнительная зависимость, дополнительная инфраструктура и дополнительная настройка, в то время как другие фреймворки справляются с этим более естественно.
Вердикт: Лучший выбор для приложений, которым нужны админ-панели, управление контентом или быстрое прототипирование с «батарейками в комплекте».
Flask
Flask — минималистичный вариант. Он дает маршрутизацию и обработку запросов и ничего больше. Каждая дополнительная функция — это решение и библиотека, которую вы подключаете.
Скорость разработки: Flask стартовал медленнее всех, потому что каждая функция требовала отдельного решения. SQLAlchemy для ORM, Flask-JWT-Extended для аутентификации, Flask-Migrate для миграций. Каждая требовала настройки и имела свои соглашения.
Производительность под нагрузкой:
- Среднее время ответа: 201 мс.
- P95: 389 мс.
- Запросов в секунду: 261.
- Доля ошибок: 0,4%.
Немного медленнее Django под нагрузкой. Flask тоже работает синхронно, и накладные расходы экосистемы расширений накапливаются при параллельных запросах.
Сложность кода: Гибкость, которая делает Flask привлекательным, оборачивается несогласованностью. Если над проектом на Flask работают четыре разработчика, часто получается четыре разных паттерна для одной и той же операции.
# Flask - более явный, но и более многословный
@app.route("/orders")
def order_list():
page = request.args.get("page", 1, type=int)
orders = Order.query.options(
joinedload(Order.user),
subqueryload(Order.items)
).paginate(page=page, per_page=20)
return jsonify({"orders": [o.to_dict() for o in orders.items]})
Где возникли сложности: Фоновые задачи потребовали той же настройки Celery, что и в Django. В ядре Flask нет нативной поддержки асинхронности.
Вердикт: Хорош для маленьких API и проектов, где вам нужен полный контроль над каждой зависимостью. Поддерживать его становится сложнее при масштабировании из-за несогласованности в кодовой базе.
FastAPI
FastAPI построен на Starlette и использует подсказки типов из Python для автоматической валидации и генерации документации. Это фреймворк, который я использую в большинстве своих продакшен-проектов.
Скорость разработки: FastAPI оказался на уровне Django по скорости старта. Модели Pydantic автоматически обрабатывают валидацию, а автоматически генерируемая документация по адресу /docs избавляет от отдельного этапа документирования.
Производительность под нагрузкой:
- Среднее время ответа: 94 мс.
- P95: 187 мс.
- Запросов в секунду: 891.
- Доля ошибок: 0,1%
FastAPI был значительно быстрее Django и Flask под параллельной нагрузкой. Асинхронная архитектура означает, что воркеры обрабатывают несколько запросов одновременно, пока ждут ответа от базы данных.
Сложность кода: Подход на основе подсказок типов сохраняет код чистым и самодокументируемым.
# FastAPI - подсказки типов делают всю тяжелую работу
@app.get("/orders", response_model=PaginatedOrders)
async def order_list(page: int = 1, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(Order).options(selectinload(Order.items))
.offset((page - 1) * 20).limit(20)
)
return {"orders": result.scalars().all()}
Где возникли сложности: Нет встроенной админ-панели. Аутентификация требует ручной реализации или подключения библиотеки. Асинхронная модель требует понимания асинхронного Python, иначе производительность страдает.
Вердикт: Лучший выбор для высоконагруженных API, микросервисов и любых приложений, где важно время ответа при параллельной нагрузке.
Starlette
Этот фреймворк меня удивил.
Starlette — это ASGI-фреймворк, на котором построен FastAPI. Большинство Python-разработчиков знают о его существовании, но немногие используют его напрямую. FastAPI добавляет валидацию через подсказки типов, автоматическую документацию и внедрение зависимостей поверх Starlette. Использовать Starlette напрямую — значит писать все это самому.
Я включил его, ожидая, что он будет непрактичен для реальных приложений. Я ошибался.
Скорость разработки: Медленнее, чем у FastAPI, потому что нет автоматической валидации или документации. Каждый эндпоинт требует явного разбора запроса и построения ответа.
Производительность под нагрузкой:
- Среднее время ответа: 71 мс.
- P95: 134 мс.
- Запросов в секунду: 1247.
- Доля ошибок: 0,05%
Starlette оказался на 40% быстрее FastAPI и более чем в 4 раза быстрее Django. Без слоя валидации и накладных расходов на внедрение зависимостей запросы проходят через стек с минимальной обработкой.
# Starlette - явно, но чрезвычайно быстро
async def order_list(request):
page = int(request.query_params.get("page", 1))
async with AsyncSession(engine) as db:
result = await db.execute(
select(Order).offset((page - 1) * 20).limit(20)
)
orders = result.scalars().all()
return JSONResponse({"orders": [o.__dict__ for o in orders]})
Где возникли сложности: Отсутствие валидации означает, что ошибки валидации становятся ошибками времени выполнения, а не автоматическими ответами 422. Нет внедрения зависимостей — общая логика требует явной передачи или глобального состояния. Чтобы создать продакшен-приложение на Starlette, нужно перестроить те компоненты, которые FastAPI предоставляет бесплатно.
Вердикт: Правильный выбор для приложений с очень высокой пропускной способностью, где важна каждая миллисекунда, и вы готовы сами реализовать валидацию и документацию. Не практичен для большинства команд.
Сравнительные результаты
Django. Среднее время ответа: 187 мс. Запросов в секунду: 284. Скорость разработки: самая быстрая. Подходит для полноценных приложений и админ-панелей.
Flask. Среднее время ответа: 201 мс. Запросов в секунду: 261. Скорость разработки: самая медленная. Подходит для небольших API и полного контроля.
FastAPI. Среднее время ответа: 94 мс. Запросов в секунду: 891. Скорость разработки: быстрая. Подходит для высоконагруженных API и микросервисов.
Starlette. Среднее время ответа: 71 мс. Запросов в секунду: 1247. Скорость разработки: медленная. Подходит для максимальной пропускной способности и производительности.
Что именно меня удивило
Я предполагал, что Starlette будет непрактичным, но бенчмарки изменили мое мнение.
Для некоторых случаев использования, особенно высоконагруженных микросервисов, где опытная команда готова взять на себя дополнительный шаблонный код ради лучшей производительности, Starlette — сильный вариант. Выигрыш в скорости на 40% по сравнению с FastAPI значителен при масштабировании.
Меня удивила цена в виде времени разработки. Эндпоинт регистрации, который занял 25 минут в FastAPI, потребовал 90 минут в Starlette, потому что валидацию и обработку ошибок пришлось писать с нуля.
Этот компромисс имеет смысл только тогда, когда выигрыш в производительности приводит к реальной экономии на инфраструктуре.
Какой фреймворк вам стоит использовать
Для большинства приложений сегодня FastAPI — правильный выбор. Преимущество в производительности над Django и Flask значительно, а опыт разработки лучше, чем у Starlette.
Используйте Django, если вам нужна админ-панель, встроенная аутентификация или готовый фреймворк с «батарейками» для контентно-ориентированного приложения.
Используйте Flask, если ваша команда обладает глубокой экспертизой во Flask, а приложение достаточно мало, чтобы проблема несогласованности не имела значения.
Используйте Starlette, если вы создаете высоконагруженный микросервис, и ваша команда достаточно опытна, чтобы построить слой валидации без структуры, которую дает FastAPI.
Читайте также:
- Django REST Framework: REST API на Python с нуля
- Полезные Python модули, которыми мало кто пользуется
- 7 библиотек Python для улучшения бэкенда
Читайте нас в Telegram, VK и Дзен
Перевод статьи inprogrammer: I Compared 4 Python Web Frameworks. One Surprised Me Completely





