FastAPI и Django под прицелом ИИ: 14 промтов, которые закрывают весь цикл Python-бэкенда — от схемы API до продакшн-деплоя
Python-бэкенд в 2026 году — это не только код, но и умение разговаривать с инструментом, который этот код пишет и проверяет вместе с тобой. FastAPI и Django остаются двумя главными полюсами: первый — для асинхронных API и микросервисов, второй — для монолитов с батарейками вроде ORM, админки и миграций. По данным опроса JetBrains Developer Ecosystem, эти два фреймворка стабильно удерживаются в топе используемых Python-веб-технологий, а Django REST Framework и Pydantic — среди самых популярных библиотек.
Проблема в том, что большинство разработчиков используют LLM как «умный автокомплит»: просят написать функцию и получают код, который падает на первом же edge case. Ниже — подборка промтов, построенных по принципу task → prompt → example result. Каждый промт — это не «сгенерируй мне CRUD», а постановка инженерной задачи с контекстом, ограничениями и критериями приёмки. Я применял их в реальных проектах: платёжный шлюз на FastAPI, внутренняя CRM на Django с тяжелыми аналитическими отчётами, миграция монолита на async-стек.
Промты разбиты на три уровня: basic (ежедневная рутина), advanced (архитектура и производительность) и expert (то, что обычно ломается в проде). Код в примерах рабочий и проверен на Python 3.12, FastAPI 0.115, Django 5.1.
Basic: фундамент, который экономит часы
1. Проектирование контракта REST API до написания кода
Task: получить черновик OpenAPI-спецификации и Pydantic-схем, чтобы не переделывать модель данных после первого спринта.
Prompt:
Ты — backend-архитектор. Спроектируй REST API для сервиса бронирования переговорных. Требования: роли user/admin, ресурсы rooms, bookings, users. Верни: (1) таблицу эндпоинтов с методами, путями, кодами ответов; (2) Pydantic v2-схемы для запросов и ответов; (3) список доменных ошибок с HTTP-кодами. Используй пагинацию cursor-based, а не offset. Не пиши реализацию — только контракт.
Example result: модель получает таблицу вида ниже и готовый app/schemas.py.
| Метод | Путь | Назначение | Успех | Ошибки |
|---|---|---|---|---|
| POST | /api/v1/bookings | создать бронь | 201 | 409 (пересечение), 422 |
| GET | /api/v1/rooms | список комнат | 200 | — |
| DELETE | /api/v1/bookings/{id} | отмена | 204 | 403, 404 |
from datetime import datetime
from pydantic import BaseModel, Field, ConfigDict
class BookingCreate(BaseModel):
room_id: int
starts_at: datetime
ends_at: datetime
title: str = Field(min_length=3, max_length=120)
model_config = ConfigDict(extra="forbid")
class BookingOut(BookingCreate):
id: int
user_id: int
extra="forbid" здесь критичен: без него клиент может прислать лишние поля, и вы узнаете об этом, только когда кто-то попытается передать is_admin: true.
2. Генерация Django-моделей с индексами и ограничениями
Task: превратить текстовое описание сущностей в модели с Meta.constraints, а не просто поля.
Prompt:
Сгенерируй Django-модели для учёта складских остатков: Product, Warehouse, Stock. Добавь: уникальное ограничение на пару (product, warehouse), индекс по полю quantity для фильтрации «что заканчивается», CheckConstraint на неотрицательный остаток. Python 3.12, Django 5.1. Покажи миграцию.
Example result:
from django.db import models
from django.db.models import Q, F
class Stock(models.Model):
product = models.ForeignKey("Product", on_delete=models.CASCADE)
warehouse = models.ForeignKey("Warehouse", on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=0)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["product", "warehouse"], name="uniq_product_warehouse"
),
models.CheckConstraint(
condition=Q(quantity__gte=0), name="quantity_non_negative"
),
]
indexes = [models.Index(fields=["quantity"], name="stock_qty_idx")]
В Django 5.1 аргумент check переименован в condition — старая формулировка всё ещё работает, но вызывает RemovedInDjango60Warning. Промт явно фиксирует версию, поэтому модель не пришлось править руками.
3. Быстрый каркас тестов на pytest
Task: получить фикстуры и набор тестов на граничные случаи, а не только happy path.
Prompt:
Напиши pytest-тесты для эндпоинта создания брони. Используй httpx.AsyncClient с ASGITransport, фабрики через factory_boy. Обязательно покрой: успешное создание, конфликт по времени (409), невалидный диапазон дат (422), бронь в прошлом. Покажи фикстуру с транзакционным откатом.
Example result:
import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
@pytest.fixture
async def client(db):
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
@pytest.mark.asyncio
async def test_booking_conflict(client, room, existing_booking):
payload = {
"room_id": room.id,
"starts_at": existing_booking.starts_at.isoformat(),
"ends_at": existing_booking.ends_at.isoformat(),
"title": "Sync",
}
r = await client.post("/api/v1/bookings", json=payload)
assert r.status_code == 409
Обратите внимание: ASGITransport из httpx позволяет тестировать ASGI-приложение без поднятия uvicorn — это быстрее и не оставляет висящих портов в CI.
4. Рефакторинг «толстого» view в сервисный слой
Task: вынести бизнес-логику из Django view в сервисы, сохранив поведение.
Prompt:
Отрефактори этот Django view: вынеси логику в слой сервисов, оставь view только парсинг запроса и формирование ответа. Сохрани текущее поведение и все побочные эффекты. Покажи до/после и объясни, какие тесты нужно добавить, чтобы зафиксировать контракт.
Example result: типичный view на 80 строк превращается в 12 строк, а логика уезжает в services/bookings.py с чистой функцией create_booking(user, data) -> Booking. Главная выгода — сервис тестируется без HTTP-клиента, а транзакция (transaction.atomic) управляется в одном месте, а не размазана по view и сигналам.
5. Генерация миграций для сложных изменений
Task: безопасно добавить NOT NULL-поле в таблицу на миллионы строк без даунтайма.
Prompt:
Нужно добавить поле
status(CharField, choices, NOT NULL, default) в таблицу на 20 млн строк в PostgreSQL. Опиши пошаговую стратегию миграции без блокировки: null=True → backfill батчами → data migration → alter на NOT NULL. Сгенерируй файлы миграций Django и SQL для батчевого обновления.
Example result: три миграции — AddField(null=True), RunPython с bulk_update по чанкам, AlterField(null=False). Ключевая деталь: RunPython с atomic = False и явным chunk_size, иначе одна транзакция на 20 млн строк положит таблицу.
Advanced: производительность и архитектура
6. Устранение N+1 запросов в Django ORM
Task: найти и починить N+1, не угадывая, а по логам запросов.
Prompt:
Вот код, который рендерит список заказов с позициями и клиентами. Он делает сотни запросов. (1) Определи источники N+1. (2) Перепиши с select_related/prefetch_related и Prefetch с to_attr. (3) Покажи, как проверить количество запросов через django.test.utils.CaptureQueriesContext.
Example result:
from django.db import connection
from django.test.utils import CaptureQueriesContext
with CaptureQueriesContext(connection) as ctx:
list(Order.objects.select_related("customer")
.prefetch_related("items__product").all())
assert len(ctx.captured_queries) <= 3
Правило, которое стоит запомнить: select_related — для ForeignKey и OneToOne (JOIN), prefetch_related — для ManyToMany и обратных связей (отдельный запрос + склейка в Python). Промт заставляет модель обосновать выбор, и вы перестаёте ставить их наугад.
7. Оптимизация медленного запроса в PostgreSQL
Task: превратить EXPLAIN ANALYZE в конкретный индекс и переписанный запрос.
Prompt:
Вот вывод EXPLAIN (ANALYZE, BUFFERS) для запроса отчёта. Найди узкое место, предложи индексы (включая частичные и составные), перепиши запрос. Учти, что таблица партиционирована по месяцам. Объясни, почему planner выбрал Seq Scan.
Example result: Seq Scan по 12 млн строк заменяется на Index Scan после CREATE INDEX CONCURRENTLY idx_orders_created_status ON orders (created_at DESC, status) WHERE status = 'paid';. Важно: CONCURRENTLY не блокирует запись, но не работает внутри транзакции — промт должен это учитывать, иначе миграция упадёт.
8. Асинхронный эндпоинт без блокировки event loop
Task: не допустить, чтобы синхронный вызов внутри async def убил производительность.
Prompt:
Отрефактори этот FastAPI-эндпоинт: внутри async def вызывается блокирующая библиотека (requests, psycopg2). Покажи два варианта — вынести в run_in_threadpool и заменить на async-аналог (httpx, asyncpg). Объясни, когда какой вариант выбрать.
Example result:
from fastapi.concurrency import run_in_threadpool
@app.get("/rates")
async def rates():
data = await run_in_threadpool(legacy_client.fetch_rates)
return data
Один блокирующий вызов в async def останавливает весь event loop — под нагрузкой это выглядит как «сервер завис без ошибок». run_in_threadpool — быстрый фикс, async-драйвер — правильное решение.
9. Кэширование с инвалидацией
Task: спроектировать кэш так, чтобы данные не протухали молча.
Prompt:
Спроектируй кэширование для каталога товаров: Redis, ключи с версионированием, TTL, инвалидация по событию изменения. Покажи реализацию на FastAPI с dependency-injection и фоновой задачей очистки. Опиши сценарий cache stampede и как его избежать.
Example result: паттерн «cache-aside» плюс блокировка на пересчёт (single-flight) через redis.lock, чтобы при промахе сотня параллельных запросов не пошла в БД одновременно. TTL — страховка, а не основной механизм: инвалидация должна быть событийной.
10. GraphQL-схема на Strawberry
Task: получить схему без N+1 на резолверах.
Prompt:
Сгенерируй GraphQL-схему на strawberry-graphql для блога: Post, Author, Comment. Покажи DataLoader для батчинга авторов по постам, чтобы избежать N+1. Продемонстрируй запрос и посчитай количество SQL-запросов.
Example result: DataLoader собирает все author_id из текущего «тика» резолверов и делает один IN-запрос вместо N. Без него GraphQL печально известен тем, что один клиентский запрос порождает сотни SQL-запросов.
Expert: прод, миграции и то, что ломается в пятницу вечером
11. Zero-downtime деплой и health checks
Task: настроить деплой так, чтобы rolling update не ронял запросы.
Prompt:
Опиши zero-downtime деплой FastAPI-приложения в Kubernetes: liveness, readiness, startup probes, graceful shutdown по SIGTERM, корректная работа с in-flight запросами. Покажи Dockerfile (multi-stage, non-root) и манифест Deployment. Учти миграции БД как отдельный Job.
Example result: uvicorn с --timeout-graceful-shutdown, readiness probe на /health/ready, который проверяет доступность БД, и отдельный Job для alembic upgrade head до старта нового ReplicaSet. Главная ошибка, которую ловит промт, — миграции внутри старта приложения: при нескольких репликах они гонятся наперегонки.
12. Структурированное логирование и трассировка
Task: сделать логи машиночитаемыми и связать их с запросами.
Prompt:
Настрой structlog для FastAPI: JSON-логи, correlation_id из заголовка X-Request-ID, контекст пользователя, интеграция с OpenTelemetry для трейсинга. Покажи middleware и пример лог-записи для медленного запроса (>500 мс).
Example result: каждая запись содержит request_id, user_id, duration_ms, path, status. По request_id вы находите всю цепочку от Nginx до SQL-запроса — без этого отладка инцидентов в проде превращается в археологию.
13. Аудит безопасности API
Task: найти типовые дыры до того, как их найдёт сканер.
Prompt:
Проведи аудит этого FastAPI/Django кода по OWASP API Security Top 10. Проверь: broken object level authorization, mass assignment, отсутствие rate limiting, утечки в ошибках, небезопасные JWT (алгоритм none, отсутствие проверки exp/aud). Для каждой находки дай исправленный фрагмент.
Example result: типичный список — extra="forbid" в схемах (mass assignment), проверка владельца объекта в зависимостях (BOLA, риск №1 по OWASP API Top 10), ограничение algorithms=["RS256"] при декодировании JWT. ИБ-находки почти всегда однотипны, и промт превращает их в чеклист.
14. Диагностика плавающего бага в проде
Task: превратить стектрейс и логи в гипотезу и воспроизводимый тест.
Prompt:
Вот стектрейс и логи за 10 минут до падения (раз в ~2 часа под нагрузкой). Сформулируй 3 гипотезы, отсортируй по вероятности, для каждой предложи способ проверки и минимальный тест, воспроизводящий баг. Учти особенности async и connection pool exhaustion.
Example result: самая частая причина таких «плавающих» падений — исчерпание пула соединений: asyncpg/SQLAlchemy ждут свободный коннект, таймаут срабатывает, а виноватым выглядит случайный эндпоинт. Промт заставляет сначала проверить метрику pool.checkedout, а не переписывать код наугад.
Что делает эти промты рабочими
Три принципа, которые отличают результат от «сгенерируй мне CRUD»:
- Контекст важнее инструкции. Версия фреймворка, размер таблицы, тип БД, ограничения инфраструктуры — без этого модель даёт учебник, а не решение.
- Критерий приёмки в промте. «Покажи, как проверить» — превращает ответ в проверяемый артефакт: тест,
EXPLAIN, метрику. - Ограничения как часть задачи. «Без даунтайма», «без блокировки», «на 20 млн строк» заставляют модель выбирать инженерный компромисс, а не первый попавшийся API.
Официальные источники, на которые стоит опираться при проверке ответов: документация FastAPI, Django и Django REST Framework, спецификация OpenAPI 3.1, OWASP API Security Top 10, RFC 7519 для JWT и документация PostgreSQL по EXPLAIN. Модель может ошибаться в деталях — но с этими ссылками вы проверяете её за минуты, а не за день.
Эти 14 промтов — не догма, а каркас. Возьмите те, что закрывают ваш узкий участок: если вы тонете в N+1 и медленных отчётах — начните с пунктов 6–7; если готовитесь к первому продакшн-деплою — с 11–12. Главное правило: чем конкретнее вы описываете ограничения реального проекта, тем меньше кода придётся переписывать после ответа модели. Попробуйте применить хотя бы три промта на текущей задаче — и сравните, сколько времени ушло на ревью сгенерированного кода против написания с нуля.
Comments