committed by
GitHub
1 changed files with 350 additions and 0 deletions
@ -0,0 +1,350 @@ |
|||
# Зависимости |
|||
|
|||
**FastAPI** имеет очень мощную и интуитивную систему **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>**. |
|||
|
|||
Она проектировалась таким образом, чтобы быть простой в использовании и облегчить любому разработчику интеграцию других компонентов с **FastAPI**. |
|||
|
|||
## Что такое "Dependency Injection" (инъекция зависимости) |
|||
|
|||
**"Dependency Injection"** в программировании означает, что у вашего кода (в данном случае, вашей *функции обработки пути*) есть способы объявить вещи, которые запрашиваются для работы и использования: "зависимости". |
|||
|
|||
И потом эта система (в нашем случае **FastAPI**) организует всё, что требуется, чтобы обеспечить ваш код этой зависимостью (сделать "инъекцию" зависимости). |
|||
|
|||
Это очень полезно, когда вам нужно: |
|||
|
|||
* Обеспечить общую логику (один и тот же алгоритм снова и снова). |
|||
* Общее соединение с базой данных. |
|||
* Обеспечение безопасности, аутентификации, запроса роли и т.п. |
|||
* И многое другое. |
|||
|
|||
Всё это минимизирует повторение кода. |
|||
|
|||
## Первые шаги |
|||
|
|||
Давайте рассмотрим очень простой пример. Он настолько простой, что на данный момент почти бесполезный. |
|||
|
|||
Но таким способом мы можем сфокусироваться на том, как же всё таки работает система **Dependency Injection**. |
|||
|
|||
### Создание зависимости или "зависимого" |
|||
Давайте для начала сфокусируемся на зависимостях. |
|||
|
|||
Это просто функция, которая может принимать все те же параметры, что и *функции обработки пути*: |
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="8-9" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="8-11" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="9-12" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="6-7" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
|
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="8-11" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
**И всё.** |
|||
|
|||
**2 строки.** |
|||
|
|||
И теперь она той же формы и структуры, что и все ваши *функции обработки пути*. |
|||
|
|||
Вы можете думать об *функции обработки пути* как о функции без "декоратора" (без `@app.get("/some-path")`). |
|||
|
|||
И она может возвращать всё, что требуется. |
|||
|
|||
В этом случае, эта зависимость ожидает: |
|||
|
|||
* Необязательный query-параметр `q` с типом `str` |
|||
* Необязательный query-параметр `skip` с типом `int`, и значением по умолчанию `0` |
|||
* Необязательный query-параметр `limit` с типом `int`, и значением по умолчанию `100` |
|||
|
|||
И в конце она возвращает `dict`, содержащий эти значения. |
|||
|
|||
!!! Информация |
|||
|
|||
**FastAPI** добавил поддержку для `Annotated` (и начал её рекомендовать) в версии 0.95.0. |
|||
|
|||
Если у вас более старая версия, будут ошибки при попытке использовать `Annotated`. |
|||
|
|||
Убедитесь, что вы [Обновили FastAPI версию](../../deployment/versions.md#upgrading-the-fastapi-versions){.internal-link target=_blank} до, как минимум 0.95.1, перед тем как использовать `Annotated`. |
|||
|
|||
### Import `Depends` |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="3" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
### Объявите зависимость в "зависимом" |
|||
|
|||
Точно так же, как вы использовали `Body`, `Query` и т.д. с вашей *функцией обработки пути* для параметров, используйте `Depends` с новым параметром: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="13 18" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="15 20" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="16 21" |
|||
{!> ../../../docs_src/dependencies/tutorial001_an.py!} |
|||
``` |
|||
|
|||
=== "Python 3.10+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="11 16" |
|||
{!> ../../../docs_src/dependencies/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+ non-Annotated" |
|||
|
|||
!!! tip "Подсказка" |
|||
Настоятельно рекомендуем использовать `Annotated` версию насколько это возможно. |
|||
|
|||
```Python hl_lines="15 20" |
|||
{!> ../../../docs_src/dependencies/tutorial001.py!} |
|||
``` |
|||
|
|||
`Depends` работает немного иначе. Вы передаёте в `Depends` одиночный параметр, который будет похож на функцию. |
|||
|
|||
Вы **не вызываете его** на месте (не добавляете скобочки в конце: 👎 *your_best_func()*👎), просто передаёте как параметр в `Depends()`. |
|||
|
|||
И потом функция берёт параметры так же, как *функция обработки пути*. |
|||
|
|||
!!! tip "Подсказка" |
|||
В следующей главе вы увидите, какие другие вещи, помимо функций, можно использовать в качестве зависимостей. |
|||
|
|||
Каждый раз, когда новый запрос приходит, **FastAPI** позаботится о: |
|||
|
|||
* Вызове вашей зависимости ("зависимого") функции с корректными параметрами. |
|||
* Получении результата из вашей функции. |
|||
* Назначении результата в параметр в вашей *функции обработки пути*. |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
common_parameters(["common_parameters"]) |
|||
read_items["/items/"] |
|||
read_users["/users/"] |
|||
|
|||
common_parameters --> read_items |
|||
common_parameters --> read_users |
|||
``` |
|||
|
|||
Таким образом, вы пишете общий код один раз, и **FastAPI** позаботится о его вызове для ваших *операций с путями*. |
|||
|
|||
!!! check "Проверка" |
|||
Обратите внимание, что вы не создаёте специальный класс и не передаёте его куда-то в **FastAPI** для регистрации, или что-то в этом роде. |
|||
|
|||
Вы просто передаёте это в `Depends`, и **FastAPI** знает, что делать дальше. |
|||
|
|||
## Объединяем с `Annotated` зависимостями |
|||
|
|||
В приведенном выше примере есть небольшое **повторение кода**. |
|||
|
|||
Когда вам нужно использовать `common_parameters()` зависимость, вы должны написать весь параметр с аннотацией типов и `Depends()`: |
|||
|
|||
```Python |
|||
commons: Annotated[dict, Depends(common_parameters)] |
|||
``` |
|||
|
|||
Но потому что мы используем `Annotated`, мы можем хранить `Annotated` значение в переменной и использовать его в нескольких местах: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12 16 21" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14 18 23" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.8+" |
|||
|
|||
```Python hl_lines="15 19 24" |
|||
{!> ../../../docs_src/dependencies/tutorial001_02_an.py!} |
|||
``` |
|||
|
|||
!!! tip "Подсказка" |
|||
Это стандартный синтаксис python и называется "type alias", это не особенность **FastAPI**. |
|||
|
|||
Но потому что **FastAPI** базируется на стандартах Python, включая `Annotated`, вы можете использовать этот трюк в вашем коде. 😎 |
|||
|
|||
|
|||
Зависимости продолжат работу как ожидалось, и **лучшая часть** в том, что **информация о типе будет сохранена**. Это означает, что ваш редактор кода будет корректно обрабатывать **автодополнения**, **встроенные ошибки** и так далее. То же самое относится и к инструментам, таким как `mypy`. |
|||
|
|||
Это очень полезно, когда вы интегрируете это в **большую кодовую базу**, используя **одинаковые зависимости** снова и снова во **многих** ***операциях пути***. |
|||
|
|||
## Использовать `async` или не `async` |
|||
|
|||
Для зависимостей, вызванных **FastAPI** (то же самое, что и ваши *функции обработки пути*), те же правила, что приняты для определения ваших функций. |
|||
|
|||
Вы можете использовать `async def` или обычное `def`. |
|||
|
|||
Вы также можете объявить зависимости с `async def` внутри обычной `def` *функции обработки пути*, или `def` зависимости внутри `async def` *функции обработки пути*, и так далее. |
|||
|
|||
Это всё не важно. **FastAPI** знает, что нужно сделать. 😎 |
|||
|
|||
!!! note "Информация" |
|||
Если вам эта тема не знакома, прочтите [Async: *"In a hurry?"*](../../async.md){.internal-link target=_blank} раздел о `async` и `await` в документации. |
|||
|
|||
## Интеграция с OpenAPI |
|||
|
|||
Все заявления о запросах, валидаторы, требования ваших зависимостей (и подзависимостей) будут интегрированы в соответствующую OpenAPI-схему. |
|||
|
|||
В интерактивной документации будет вся информация по этим зависимостям тоже: |
|||
|
|||
<img src="/img/tutorial/dependencies/image01.png"> |
|||
|
|||
## Простое использование |
|||
|
|||
Если вы посмотрите на фото, *функция обработки пути* объявляется каждый раз, когда вычисляется путь, и тогда **FastAPI** позаботится о вызове функции с корректными параметрами, извлекая информацию из запроса. |
|||
|
|||
На самом деле, все (или большинство) веб-фреймворков работают по схожему сценарию. |
|||
|
|||
Вы никогда не вызываете эти функции на месте. Их вызовет ваш фреймворк (в нашем случае, **FastAPI**). |
|||
|
|||
С системой Dependency Injection, вы можете сообщить **FastAPI**, что ваша *функция обработки пути* "зависит" от чего-то ещё, что должно быть извлечено перед вашей *функцией обработки пути*, и **FastAPI** позаботится об извлечении и инъекции результата. |
|||
|
|||
Другие распространённые термины для описания схожей идеи "dependency injection" являются: |
|||
|
|||
- ресурсность |
|||
- доставка |
|||
- сервисность |
|||
- инъекция |
|||
- компонентность |
|||
|
|||
## **FastAPI** подключаемые модули |
|||
|
|||
Инъекции и модули могут быть построены с использованием системы **Dependency Injection**. Но на самом деле, **нет необходимости создавать новые модули**, просто используя зависимости, можно объявить бесконечное количество интеграций и взаимодействий, которые доступны вашей *функции обработки пути*. |
|||
|
|||
И зависимости могут быть созданы очень простым и интуитивным способом, что позволяет вам просто импортировать нужные пакеты Python и интегрировать их в API функции за пару строк. |
|||
|
|||
Вы увидите примеры этого в следующих главах о реляционных и NoSQL базах данных, безопасности и т.д. |
|||
|
|||
## Совместимость с **FastAPI** |
|||
|
|||
Простота Dependency Injection делает **FastAPI** совместимым с: |
|||
|
|||
- всеми реляционными базами данных |
|||
- NoSQL базами данных |
|||
- внешними пакетами |
|||
- внешними API |
|||
- системами авторизации, аутентификации |
|||
- системами мониторинга использования API |
|||
- системами ввода данных ответов |
|||
- и так далее. |
|||
|
|||
## Просто и сильно |
|||
|
|||
Хотя иерархическая система Dependency Injection очень проста для описания и использования, она по-прежнему очень мощная. |
|||
|
|||
Вы можете описывать зависимости в очередь, и они уже будут вызываться друг за другом. |
|||
|
|||
Когда иерархическое дерево построено, система **Dependency Injection** берет на себя решение всех зависимостей для вас (и их подзависимостей) и обеспечивает (инъектирует) результат на каждом шаге. |
|||
|
|||
Например, у вас есть 4 API-эндпоинта (*операции пути*): |
|||
|
|||
- `/items/public/` |
|||
- `/items/private/` |
|||
- `/users/{user_id}/activate` |
|||
- `/items/pro/` |
|||
|
|||
Тогда вы можете требовать разные права для каждого из них, используя зависимости и подзависимости: |
|||
|
|||
```mermaid |
|||
graph TB |
|||
|
|||
current_user(["current_user"]) |
|||
active_user(["active_user"]) |
|||
admin_user(["admin_user"]) |
|||
paying_user(["paying_user"]) |
|||
|
|||
public["/items/public/"] |
|||
private["/items/private/"] |
|||
activate_user["/users/{user_id}/activate"] |
|||
pro_items["/items/pro/"] |
|||
|
|||
current_user --> active_user |
|||
active_user --> admin_user |
|||
active_user --> paying_user |
|||
|
|||
current_user --> public |
|||
active_user --> private |
|||
admin_user --> activate_user |
|||
paying_user --> pro_items |
|||
``` |
|||
|
|||
## Интегрировано с **OpenAPI** |
|||
|
|||
Все эти зависимости, объявляя свои требования, также добавляют параметры, проверки и т.д. к вашим операциям *path*. |
|||
|
|||
**FastAPI** позаботится о добавлении всего этого в схему открытого API, чтобы это отображалось в системах интерактивной документации. |
Loading…
Reference in new issue