Python
December 8, 2024

Знакомство с зависимостями (Depends) в FastAPI

FastAPI предоставляет мощный инструмент для работы с зависимостями — функцию Depends, которая позволяет организовать повторно используемую логику, обрабатывать параметры, управлять состоянием и легко интегрировать внешние сервисы. Зависимости облегчают разработку сложных приложений, помогая структурировать код и избегать дублирования.

В этой статье мы рассмотрим основные возможности Depends, примеры использования и способы их настройки.

Что такое зависимости в FastAPI?

Зависимости (Dependencies) в FastAPI — это способ определить общий функционал, который можно подключить к маршрутам (endpoints). Зависимости позволяют:

  • Валидировать параметры запросов,
  • Обеспечивать авторизацию и аутентификацию,
  • Подключать базы данных или другие сервисы,
  • Выполнять любую повторно используемую логику.

FastAPI автоматически вызывает зависимости, разрешает их и передаёт результаты в функции обработчиков.

Представь, что ты — кот, и тебе нужен человек, чтобы открыть банку корма. Ты не открываешь каждую банку сам (да у тебя и лапки!). Вместо этого ты зовёшь человека: "Мяу!" — и он делает это за тебя.

Depends в FastAPI работает так же! Ты просто говоришь: "Мне нужен кто-то, чтобы сделать это за меня", а FastAPI зовёт "человека" (функцию) и передаёт тебе готовый результат.

Удобно, правда? Мяу! 🐱✨

Простой пример зависимости

Начнём с простого примера: мы создадим зависимость, которая возвращает значение, и подключим её к маршруту.

from fastapi import Depends, FastAPI

app = FastAPI()

def common_dependency():
    return {"message": "Hello from dependency!"}

@app.get("/")
def read_root(dep=Depends(common_dependency)):
    return dep

Что здесь происходит:

  1. Функция common_dependency возвращает объект.
  2. Depends(common_dependency) подключает эту зависимость к маршруту.
  3. Возвращённое значение из зависимости ({"message": "Hello from dependency!"}) передаётся в обработчик.

Запрос GET / вернёт:

{"message": "Hello from dependency!"}

Зависимости с параметрами

Вы можете передавать параметры в зависимости. Например, создадим зависимость для проверки доступа, используя параметр token из заголовков.

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()

def get_token_header(token: str = Header(...)):
    if token != "secret-token":
        raise HTTPException(status_code=403, detail="Invalid token")
    return token

@app.get("/protected/")
def protected_route(token=Depends(get_token_header)):
    return {"message": "Access granted"}

Объяснение:

  1. Зависимость get_token_header получает заголовок token.
  2. Если токен неправильный, вызывается ошибка HTTPException.
  3. Если токен корректен, маршрут возвращает {"message": "Access granted"}.

Зависимости с состоянием (stateful dependencies)

Для работы с объектами, которые нужно сохранять между запросами (например, подключение к базе данных), можно использовать зависимости с состоянием. Обычно это делается с помощью контекстных менеджеров или специальных классов.

Пример: подключение к базе данных

from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session

app = FastAPI()

# Фейковая функция для создания сессии базы данных
def get_db():
    db = Session()  # Подключение к базе данных
    try:
        yield db  # Возвращаем сессию
    finally:
        db.close()  # Закрываем сессию после завершения работы

@app.get("/items/")
def read_items(db: Session = Depends(get_db)):
    return {"db_status": "Connected"}

Объяснение:

  • get_db создаёт и возвращает сессию базы данных с помощью yield.
  • После завершения запроса выполняется блок finally, который закрывает сессию.
  • Depends(get_db) передаёт объект db в обработчик.

Использование классов как зависимостей

Зависимости можно организовать в виде классов, что удобно для работы с конфигурациями или общими данными.

Пример: зависимости через класс

from fastapi import Depends, FastAPI

app = FastAPI()

class Config:
    def __init__(self, env: str):
        self.env = env

def get_config(env: str = "production"):
    return Config(env)

@app.get("/config/")
def get_config_route(config: Config = Depends(get_config)):
    return {"environment": config.env}

Объяснение:

  • Класс Config описывает конфигурацию.
  • Функция get_config возвращает экземпляр класса, и его можно настроить через параметры (например, env).

Запрос к /config/ вернёт:

{"environment": "production"}

Зависимости для повторного использования

FastAPI позволяет повторно использовать зависимости между разными маршрутами или группами маршрутов с помощью APIRouter.

from fastapi import APIRouter, Depends, FastAPI

app = FastAPI()
router = APIRouter()

def common_dependency():
    return {"message": "Shared dependency"}

@router.get("/route1/")
def route1(dep=Depends(common_dependency)):
    return dep

@router.get("/route2/")
def route2(dep=Depends(common_dependency)):
    return dep

app.include_router(router)

Объяснение:

  • Зависимость common_dependency используется в обоих маршрутах.
  • APIRouter помогает группировать маршруты и подключать зависимости для всей группы.

Сложные зависимости: вложенность

Зависимости могут быть вложенными. Например, одна зависимость может использовать другую.

Пример вложенной зависимости

def verify_token(token: str = Header(...)):
    if token != "secret-token":
        raise HTTPException(status_code=403, detail="Invalid token")
    return token

def get_current_user(token: str = Depends(verify_token)):
    return {"username": "test_user"}

@app.get("/users/me")
def read_current_user(user=Depends(get_current_user)):
    return user

Объяснение:

  • verify_token проверяет токен.
  • get_current_user использует токен для получения текущего пользователя.
  • Обработчик read_current_user получает данные пользователя из зависимости get_current_user.

Зависимости с асинхронными функциями

FastAPI полностью поддерживает асинхронные зависимости. Это полезно для операций, таких как асинхронное подключение к базе данных или вызовы внешних API.

import asyncio

async def async_dependency():
    await asyncio.sleep(1)  # Имитация длительной операции
    return {"status": "Success"}

@app.get("/async/")
async def async_route(dep=Depends(async_dependency)):
    return dep

Заключение

FastAPI делает работу с зависимостями гибкой и удобной. Depends позволяет организовывать повторно используемую логику, проверять параметры, подключать внешние сервисы и управлять состоянием приложения. Вот ключевые моменты:

  1. Простые зависимости облегчают проверку данных и выполнение общей логики.
  2. Контекстные зависимости подходят для управления подключениями, например, к базе данных.
  3. Классы-зависимости делают код более структурированным и масштабируемым.
  4. Асинхронные зависимости полезны для высоконагруженных приложений.

Использование зависимостей помогает сделать ваш код чище, API — надёжнее, а разработку — проще. FastAPI снова показывает, почему он такой удобный инструмент для построения сложных веб-приложений! 🚀