Гайды

Внедрение зависимостей в Python и FastAPI Depends

Зачем DI, Depends и yield, контейнеры, dependency_overrides в тестах и антипаттерн глобалов.

~9 мин чтения

Внедрение зависимостей в Python и FastAPI Depends

В Python внедрение зависимостей чаще явное: функции/классы получают зависимости аргументами или через контейнер при старте. В FastAPIDepends. Параллель в других стеках — конструкторы в Spring, Django сервис-слой. Маршруты и DI в FastAPI — Маршрутизация и Dependency Injection; контейнеры приложения — Docker: контейнеризация (не путать с IoC-контейнером).


1. Зачем DI

Тестируемость (моки), единая точка создания DB pool, config, HTTP client с lifecycle.


2. FastAPI Depends

python
def get_repo() -> OrderRepo:
    return SqlOrderRepo(pool)

@app.post("/orders")
def create(repo: OrderRepo = Depends(get_repo), body: OrderCreate = ...):
    return repo.create(body)

Depends на классе__call__ без параметров для «класс-зависимость».

Сессия БД с yield — стандартный паттерн: создать сессию, отдать в обработчик, после ответа закрыть/откатить при ошибке:

python
from collections.abc import Generator
from sqlalchemy.orm import Session

def get_session() -> Generator[Session, None, None]:
    session = SessionLocal()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()

@app.get("/items")
def list_items(session: Session = Depends(get_session)):
    return session.query(Item).all()

FastAPI вызывает генератор до yield при входе в запрос и код после yield при завершении (в т.ч. после отправки ответа для sync-эндпоинтов — см. документацию по async и блокирующим операциям в teardown).


3. Контейнеры в Python

dependency-injector, punq, wired — если нужна граф-зависимостей крупнее, чем удобно выразить Depends.


4. Антипаттерн: глобалы

Модули уровня db = connect() при импорте усложняют тесты и переиспользование.


5. Вложенные Depends и sub-dependencies

Depends(get_session) внутри get_repo(session=Depends(get_session)) строит граф автоматически; кэширование одного запроса — один вызов get_session на request. Используйте Annotated (Annotated[Session, Depends(get_session)]) для читаемости в Python 3.9+.


6. Security scopes и контекст

Security(HTTPBearer()) с scopes комбинируется с Depends для проверки прав до бизнес-логики. Выносите разбор JWT в одну зависимость, а проверку ролей — в отдельную, чтобы переиспользовать в роутерах.


7. Чек-лист

  • Зависимости с yield для ресурсов — корректный teardown.
  • Не тащить request-scoped данные в singleton без очистки.
  • В тестах — app.dependency_overrides.
  • Не выполнять тяжёлую работу в Depends без кэша на уровень запроса.
  • Документировать в OpenAPI через dependencies=[] на роутере при общих требованиях.

Дальше: Быстрый старт с FastAPI