FastAPI и Django под прицелом ИИ: 14 промтов, которые закрывают весь цикл Python-бэкенда — от схемы API до продакшн-деплоя

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»:

  1. Контекст важнее инструкции. Версия фреймворка, размер таблицы, тип БД, ограничения инфраструктуры — без этого модель даёт учебник, а не решение.
  2. Критерий приёмки в промте. «Покажи, как проверить» — превращает ответ в проверяемый артефакт: тест, EXPLAIN, метрику.
  3. Ограничения как часть задачи. «Без даунтайма», «без блокировки», «на 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. Главное правило: чем конкретнее вы описываете ограничения реального проекта, тем меньше кода придётся переписывать после ответа модели. Попробуйте применить хотя бы три промта на текущей задаче — и сравните, сколько времени ушло на ревью сгенерированного кода против написания с нуля.

← All posts

Comments