Книга: FastAPI: веб-разработка на Python
Назад: Глава 5. Pydantic, подсказки типов и обзор моделей
Дальше: Глава 7. Сравнение фреймворков

Глава 6. Зависимости

Обзор

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

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

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

При написании веб-сервиса в какой-то момент вам может понадобиться сделать следующее:

• получить входные параметры из HTTP-запроса;

• проверить вводимые данные;

• проверить аутентификацию и авторизацию пользователей для некоторых конечных точек;

• найти данные в источнике данных, часто в базе данных;

• выдавать параметры, журналы или информацию для отслеживания.

Веб-фреймворки преобразуют байты HTTP-запросов в структуры данных, а вы по мере необходимости извлекаете из них то, что вам нужно, в своих функциях веб-уровня.

Проблемы с зависимостями

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

Тестирование — вы не можете протестировать варианты функции, выполняющие поиск зависимостей по-другому.

• Скрытые зависимости — сокрытие подробной информации означает, что код, необходимый вашей функции, может прерваться при изменении внешнего кода.

• Дублирование кода — если зависимость является общей (например, поиск пользователя в базе данных или объединение значений из HTTP-запроса), код поиска может оказаться продублированным в нескольких функциях.

Видимость OpenAPI — автоматическая тестовая страница, создаваемая FastAPI, нуждается в информации из механизма внедрения зависимостей.

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

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

Зависимости FastAPI

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

Вы уже видели некоторые зависимости, правда, прежде их так не называли, — это источники данных HTTP, такие как Path, Query, Body и Header. Это функции или классы Python, откапывающие запрашиваемые данные из различных областей HTTP-запроса. Они скрывают детали, такие как проверка валидности и форматы данных.

Почему бы не написать собственные функции для этого? Можно, но у вас не будет:

• проверки валидности данных;

• преобразования форматов;

• автоматического документирования.

Во многих других веб-фреймворках эти проверки выполняются внутри собственных функций. Примеры их работы приведены в главе 7, где FastAPI сравнивается с такими веб-фреймворками Python, как Flask и Django. Но в FastAPI можно работать с собственными зависимостями так же, как и со встроенными.

Написание зависимостей

В FastAPI зависимость — это то, что выполняется, поэтому объект зависимости должен относиться к типу Callable, включающему функции и классы — то, что вы вызываете, со скобками и необязательными аргументами.

В примере 6.1 показана функция зависимости user_dep(). Она принимает строковые аргументы имени и пароля и просто возвращает значение True, если пользователь прошел проверку на валидность. Для этой первой версии пусть функция возвращает значение True для всех данных.

Пример 6.1. Функция зависимости

from fastapi import FastAPI, Depends, Params

app = FastAPI()

# функция зависимости:

def user_dep(name: str = Params, password: str = Params):

    return {"name": name, "valid": True}

# функция пути/конечная точка веб-приложения:

@app.get("/user")

def get_user(user: dict = Depends(user_dep)) -> dict:

    return user

В этом фрагменте кода user_dep() — функция зависимости. Она действует как функция пути FastAPI (знает о таких вещах, как Params и т.д.), но не содержит декоратора пути над собой. Это помощник, а не сама конечная точка.

В функции пути get_user() говорится, что она ожидает переменную аргумента под названием user и эта переменная получит свое значение из функции зависимости user_dep().

В аргументах функции get_user() нельзя написать user = user_dep, потому что user_dep — это объект функции Python. И нельзя написать user = user_dep(), потому что это вызвало бы функцию user_dep(), когда функция get_user() была определена, а не когда она используется. Поэтому нам нужна дополнительная вспомогательная функция FastAPI Depends(), чтобы вызывать user_dep() именно тогда, когда это необходимо.

В списке аргументов функции пути может быть несколько зависимостей.

Область действия зависимости

Вы можете определить зависимости для одной функции пути, их группы или всего веб-приложения.

Единый путь

Включите в функцию пути такой аргумент:

def pathfunc(name: depfunc = Depends(depfunc)):

или просто в таком виде:

def pathfunc(name: depfunc = Depends()):

name — это то, как вы хотите назвать значение (значения), возвращаемое depfunc. Из предыдущего примера:

pathfunc — это get_user();

• depfunc — это user_dep();

name — это user.

Пример 6.2 учитывает этот путь и зависимость для возврата фиксированного имени (name) пользователя и логического значения valid.

Пример 6.2. Возвращение зависимости пользователя

from fastapi import FastAPI, Depends, Params

app = FastAPI()

# функция зависимости:

def user_dep(name: str = Params, password: str = Params):

    return {"name": name, "valid": True}

# функция пути/конечная точка веб-приложения:

@app.get("/user")

def get_user(user: dict = Depends(user_dep)) -> dict:

    return user

Если функция зависимости просто проверяет что-то и не возвращает никаких значений, вы можете определить зависимость в декораторе пути (предыдущая строка, начинающаяся с @):

@app.method(url, dependencies=[Depends(depfunc)])

Попробуем сделать это в примере 6.3.

Пример 6.3. Определение зависимости проверки пользователя

from fastapi import FastAPI, Depends, Params

app = FastAPI()

# функция зависимости:

def check_dep(name: str = Params, password: str = Params):

    if not name:

        raise

# функция пути/конечная точка веб-приложения:

@app.get("/check_user", dependencies=[Depends(check_dep)])

def check_user() -> bool:

    return True

Множество путей

В главе 9 подробно рассказывается о том, как структурировать более крупное приложение FastAPI, включая определение нескольких объектов маршрутизатора (router) в приложении верхнего уровня, вместо того чтобы прикреплять каждую конечную точку к этому приложению. Пример 6.4 иллюстрирует эту концепцию.

Пример 6.4. Определение зависимости субмаршрута

from fastapi import FastAPI, Depends, APIRouter

router = APIRouter(..., dependencies=[Depends(depfunc)])

Это приведет к вызову функции depfunc() для всех функций пути ниже объекта router.

Способ глобального внедрения зависимостей

При определении объекта приложения FastAPI верхнего уровня можно добавить к нему зависимости, применяемые ко всем его функциям пути, как показано в примере 6.5.

Пример 6.5. Определение зависимости уровня приложения

from fastapi import FastAPI, Depends

def depfunc1():

    pass

def depfunc2():

    pass

app = FastAPI(dependencies=[Depends(depfunc1), Depends(depfunc2)])

@app.get("/main")

def get_main():

    pass

В этом случае используется инструкция pass, позволяющая проигнорировать другие детали, чтобы показать, как подключить зависимости.

Заключение

В этой главе мы обсудили зависимости и их внедрение — способы получения необходимых вам данных в нужный момент и простым способом. В следующей главе Flask, Django и FastAPI заходят в бар…

Назад: Глава 5. Pydantic, подсказки типов и обзор моделей
Дальше: Глава 7. Сравнение фреймворков