fastapi/docs/ru/docs/advanced/advanced-dependencies.md

164 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Продвинутые зависимости { #advanced-dependencies }
## Параметризованные зависимости { #parameterized-dependencies }
Все зависимости, которые мы видели, — это конкретная функция или класс.
Но бывают случаи, когда нужно задавать параметры зависимости, не объявляя много разных функций или классов.
Представим, что нам нужна зависимость, которая проверяет, содержит ли query-параметр `q` некоторое фиксированное содержимое.
Но при этом мы хотим иметь возможность параметризовать это фиксированное содержимое.
## «Вызываемый» экземпляр { #a-callable-instance }
В Python есть способ сделать экземпляр класса «вызываемым» объектом.
Не сам класс (он уже является вызываемым), а экземпляр этого класса.
Для этого объявляем метод `__call__`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
В этом случае именно `__call__` **FastAPI** использует для проверки дополнительных параметров и подзависимостей, и именно он будет вызван, чтобы позже передать значение параметру в вашей *функции-обработчике пути*.
## Параметризуем экземпляр { #parameterize-the-instance }
Теперь мы можем использовать `__init__`, чтобы объявить параметры экземпляра, с помощью которых будем «параметризовать» зависимость:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
В этом случае **FastAPI** вовсе не трогает `__init__` и не зависит от него — мы используем его напрямую в нашем коде.
## Создаём экземпляр { #create-an-instance }
Мы можем создать экземпляр этого класса так:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
Так мы «параметризуем» нашу зависимость: теперь внутри неё хранится "bar" в атрибуте `checker.fixed_content`.
## Используем экземпляр как зависимость { #use-the-instance-as-a-dependency }
Затем мы можем использовать этот `checker` в `Depends(checker)` вместо `Depends(FixedContentQueryChecker)`, потому что зависимостью является экземпляр `checker`, а не сам класс.
И при разрешении зависимости **FastAPI** вызовет `checker` примерно так:
```Python
checker(q="somequery")
```
…и передаст возвращённое значение как значение зависимости в нашу *функцию-обработчике пути* в параметр `fixed_content_included`:
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
/// tip | Совет
Все это может показаться притянутым за уши. И пока может быть не совсем понятно, чем это полезно.
Эти примеры намеренно простые, но они показывают, как всё устроено.
В главах про безопасность есть вспомогательные функции, реализованные тем же способом.
Если вы поняли всё выше, вы уже знаете, как «под капотом» работают эти утилиты для безопасности.
///
## Зависимости с `yield`, `HTTPException`, `except` и фоновыми задачами { #dependencies-with-yield-httpexception-except-and-background-tasks }
/// warning | Предупреждение
Скорее всего, вам не понадобятся эти технические детали.
Они полезны главным образом, если у вас было приложение FastAPI версии ниже 0.121.0 и вы столкнулись с проблемами зависимостей с `yield`.
///
Зависимости с `yield` со временем изменялись, чтобы учитывать разные случаи применения и исправлять проблемы. Ниже — краткое резюме изменений.
### Зависимости с `yield` и `scope` { #dependencies-with-yield-and-scope }
В версии 0.121.0 FastAPI добавил поддержку `Depends(scope="function")` для зависимостей с `yield`.
При использовании `Depends(scope="function")` код после `yield` выполняется сразу после завершения *функции-обработчика пути*, до отправки ответа клиенту.
А при использовании `Depends(scope="request")` (значение по умолчанию) код после `yield` выполняется после отправки ответа.
Подробнее читайте в документации: [Зависимости с `yield` — раннее завершение и `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
### Зависимости с `yield` и `StreamingResponse`, технические детали { #dependencies-with-yield-and-streamingresponse-technical-details }
До FastAPI 0.118.0, если вы использовали зависимость с `yield`, код после `yield` выполнялся после возврата из *функции-обработчика пути*, но прямо перед отправкой ответа.
Идея состояла в том, чтобы не удерживать ресурсы дольше необходимого, пока ответ «путешествует» по сети.
Это изменение также означало, что если вы возвращали `StreamingResponse`, код после `yield` в зависимости уже успевал выполниться.
Например, если у вас была сессия базы данных в зависимости с `yield`, `StreamingResponse` не смог бы использовать эту сессию во время стриминга данных, потому что сессия уже была закрыта в коде после `yield`.
В версии 0.118.0 это поведение было возвращено к тому, что код после `yield` выполняется после отправки ответа.
/// info | Информация
Как вы увидите ниже, это очень похоже на поведение до версии 0.106.0, но с несколькими улучшениями и исправлениями краевых случаев.
///
#### Сценарии с ранним выполнением кода после `yield` { #use-cases-with-early-exit-code }
Есть некоторые сценарии со специфическими условиями, которым могло бы помочь старое поведение — выполнение кода после `yield` перед отправкой ответа.
Например, представьте, что вы используете сессию базы данных в зависимости с `yield` только для проверки пользователя, а в самой *функции-обработчике пути* эта сессия больше не используется, и при этом ответ отправляется долго, например, это `StreamingResponse`, который медленно отправляет данные и по какой-то причине не использует базу данных.
В таком случае сессия базы данных будет удерживаться до завершения отправки ответа, хотя если вы её не используете, удерживать её не требуется.
Это могло бы выглядеть так:
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
Код после `yield`, автоматическое закрытие `Session` в:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
…будет выполнен после того, как ответ закончит отправку медленных данных:
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
Но поскольку `generate_stream()` не использует сессию базы данных, нет реальной необходимости держать сессию открытой во время отправки ответа.
Если у вас именно такой сценарий с SQLModel (или SQLAlchemy), вы можете явно закрыть сессию, когда она больше не нужна:
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
Так сессия освободит подключение к базе данных, и другие запросы смогут его использовать.
Если у вас есть другой сценарий, где нужно раннее завершение зависимости с `yield`, пожалуйста, создайте <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">вопрос в GitHub Discussions</a> с описанием конкретного кейса и почему вам было бы полезно иметь раннее закрытие для зависимостей с `yield`.
Если появятся веские причины для раннего закрытия в зависимостях с `yield`, я рассмотрю добавление нового способа опционально включать раннее закрытие.
### Зависимости с `yield` и `except`, технические детали { #dependencies-with-yield-and-except-technical-details }
До FastAPI 0.110.0, если вы использовали зависимость с `yield`, затем перехватывали исключение с `except` в этой зависимости и не пробрасывали исключение снова, исключение автоматически пробрасывалось дальше к обработчикам исключений или к обработчику внутренней ошибки сервера.
В версии 0.110.0 это было изменено, чтобы исправить неконтролируемое потребление памяти из‑за проброшенных исключений без обработчика (внутренние ошибки сервера) и привести поведение в соответствие с обычным поведением Python-кода.
### Фоновые задачи и зависимости с `yield`, технические детали { #background-tasks-and-dependencies-with-yield-technical-details }
До FastAPI 0.106.0 вызывать исключения после `yield` было невозможно: код после `yield` в зависимостях выполнялся уже после отправки ответа, поэтому [Обработчики исключений](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} к тому моменту уже отработали.
Так было сделано в основном для того, чтобы можно было использовать те же объекты, «отданные» зависимостями через `yield`, внутри фоновых задач, потому что код после `yield` выполнялся после завершения фоновых задач.
В FastAPI 0.106.0 это изменили, чтобы не удерживать ресурсы, пока ответ передаётся по сети.
/// tip | Совет
Кроме того, фоновая задача обычно — это самостоятельный фрагмент логики, который следует обрабатывать отдельно, со своими ресурсами (например, со своим подключением к базе данных).
Так код, скорее всего, будет чище.
///
Если вы полагались на прежнее поведение, теперь ресурсы для фоновых задач следует создавать внутри самой фоновой задачи и использовать внутри неё только данные, которые не зависят от ресурсов зависимостей с `yield`.
Например, вместо использования той же сессии базы данных, создайте новую сессию в фоновой задаче и получите объекты из базы данных с помощью этой новой сессии. И затем, вместо передачи объекта из базы данных параметром в функцию фоновой задачи, передавайте идентификатор этого объекта и заново получайте объект внутри функции фоновой задачи.