11 KiB
Классы как зависимости
Прежде чем углубиться в систему Внедрения Зависимостей, давайте обновим предыдущий пример.
dict из предыдущего примера
В предыдущем примере мы возвращали dict из нашей зависимости («dependable»):
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *}
Но затем мы получаем dict в параметре commons функции-обработчика пути.
И мы знаем, что редакторы кода не могут обеспечить достаточную поддержку (например, автозавершение) для dict, поскольку они не могут знать их ключи и типы значений.
Мы можем сделать лучше...
Что делает зависимость
До сих пор вы видели зависимости, объявленные как функции.
Но это не единственный способ объявления зависимостей (хотя он, вероятно, более распространенный).
Ключевым фактором является то, что зависимость должна быть «вызываемой».
В Python «вызываемый» — это всё, что Python может «вызвать», как функцию.
Так, если у вас есть объект something (который может и не быть функцией) и вы можете «вызвать» его (выполнить) так:
something()
или
something(some_argument, some_keyword_argument="foo")
в таком случае он является «вызываемым».
Классы как зависимости
Вы можете заметить, что для создания экземпляра класса в Python используется тот же синтаксис.
Например:
class Cat:
def __init__(self, name: str):
self.name = name
fluffy = Cat(name="Mr Fluffy")
В данном случае fluffy является экземпляром класса Cat.
А чтобы создать fluffy, вы «вызываете» Cat.
Таким образом, класс в Python также является вызываемым.
Тогда в FastAPI в качестве зависимости можно использовать класс Python.
На самом деле FastAPI проверяет, что переданный объект является «вызываемым» (функция, класс или что-либо еще) и какие параметры у него определены.
Если вы передаёте «вызываемый» объект в качестве зависимости в FastAPI, он проанализирует параметры, необходимые для этого «вызываемого» объекта, и обработает их так же, как параметры функции-обработчика пути. Включая подзависимости.
Это относится и к вызываемым объектам без параметров. Работа с ними происходит точно так же, как и для функций-обработчиков пути без параметров.
Теперь мы можем изменить зависимость («dependable») common_parameters, указанную выше, на класс CommonQueryParams:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *}
Обратите внимание на метод __init__, используемый для создания экземпляра класса:
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *}
...он имеет те же параметры, что и ранее используемая функция common_parameters:
{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *}
Эти параметры и будут использоваться FastAPI для «решения» зависимости.
В обоих случаях она будет иметь:
- Необязательный параметр запроса
q, представляющий собойstr. - Параметр запроса
skip, представляющий собойint, по умолчанию0. - Параметр запроса
limit, представляющий собойint, по умолчанию100.
В обоих случаях данные будут конвертированы, валидированы, задокументированы в схеме OpenAPI и т.д.
Как это использовать
Теперь вы можете объявить свою зависимость, используя этот класс.
{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *}
FastAPI вызывает класс CommonQueryParams. При этом создается «экземпляр» этого класса, который будет передан в качестве параметра commons в вашу функцию.
Аннотация типа и Depends
Обратите внимание, что в приведенном выше коде мы два раза пишем CommonQueryParams:
//// tab | Python 3.10+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons: CommonQueryParams = Depends(CommonQueryParams)
////
Последний CommonQueryParams, в:
... Depends(CommonQueryParams)
...это то, что FastAPI будет использовать, чтобы узнать, что является зависимостью.
Из него FastAPI извлечёт объявленные параметры, и именно его FastAPI будет вызывать.
В этом случае первый CommonQueryParams, в:
//// tab | Python 3.10+
commons: Annotated[CommonQueryParams, ...
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons: CommonQueryParams ...
////
...не имеет никакого специального значения для FastAPI. FastAPI не будет использовать его для преобразования данных, валидации и т.д. (поскольку для этого используется Depends(CommonQueryParams)).
На самом деле можно написать просто:
//// tab | Python 3.10+
commons: Annotated[Any, Depends(CommonQueryParams)]
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons = Depends(CommonQueryParams)
////
...как тут:
{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *}
Но объявление типа приветствуется, так как в этом случае ваш редактор кода будет знать, что будет передано в качестве параметра commons, и тогда он сможет помочь вам с автозавершением, проверкой типов и т.д.:
Сокращение
Но вы видите, что здесь мы имеем некоторое повторение кода, дважды написав CommonQueryParams:
//// tab | Python 3.10+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons: CommonQueryParams = Depends(CommonQueryParams)
////
FastAPI предоставляет сокращение для таких случаев, когда зависимость — это конкретный класс, который FastAPI будет «вызывать» для создания экземпляра этого класса.
Для этих конкретных случаев вы можете сделать следующее.
Вместо того чтобы писать:
//// tab | Python 3.10+
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons: CommonQueryParams = Depends(CommonQueryParams)
////
...следует написать:
//// tab | Python 3.10+
commons: Annotated[CommonQueryParams, Depends()]
////
//// tab | Python 3.10+ без Annotated
/// tip | Подсказка
Рекомендуется использовать версию с Annotated, если возможно.
///
commons: CommonQueryParams = Depends()
////
Вы объявляете зависимость как тип параметра и используете Depends() без какого-либо параметра, вместо того чтобы снова писать полный класс внутри Depends(CommonQueryParams).
Аналогичный пример будет выглядеть следующим образом:
{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *}
...и FastAPI будет знать, что делать.
/// tip | Подсказка
Если это покажется вам более запутанным, чем полезным, не обращайте внимания — это вам не нужно.
Это просто сокращение. Потому что FastAPI заботится о том, чтобы помочь вам свести к минимуму повторение кода.
///