mirror of
https://github.com/fastapi/fastapi.git
synced 2026-01-11 07:28:11 -05:00
Compare commits
2 Commits
master
...
translate-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c150294c9 | ||
|
|
455ed5dda3 |
@@ -1,8 +1,8 @@
|
||||
# Тестовый файл LLM { #llm-test-file }
|
||||
|
||||
Этот документ проверяет, понимает ли <abbr title="Large Language Model – Большая языковая модель">LLM</abbr>, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`.
|
||||
Этот документ проверяет, понимает ли <abbr title="Large Language Model - Большая языковая модель">LLM</abbr>, переводящая документацию, `general_prompt` в `scripts/translate.py` и языковой специфичный промпт в `docs/{language code}/llm-prompt.md`. Языковой специфичный промпт добавляется к `general_prompt`.
|
||||
|
||||
Тесты, добавленные здесь, увидят все создатели языковых промптов.
|
||||
Тесты, добавленные здесь, увидят все создатели языковых специфичных промптов.
|
||||
|
||||
Использование:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Проверьте, всё ли в порядке в переводе.
|
||||
* При необходимости улучшите ваш языковой специфичный промпт, общий промпт или английский документ.
|
||||
* Затем вручную исправьте оставшиеся проблемы в переводе, чтобы он был хорошим.
|
||||
* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт максимально хороши (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLM — недетерминированные алгоритмы</a>).
|
||||
* Переведите заново, имея хороший перевод на месте. Идеальным результатом будет ситуация, когда LLM больше не вносит изменений в перевод. Это означает, что общий промпт и ваш языковой специфичный промпт настолько хороши, насколько это возможно (иногда он будет делать несколько, казалось бы, случайных изменений, причина в том, что <a href="https://doublespeak.chat/#/handbook#deterministic-output" class="external-link" target="_blank">LLM — недетерминированные алгоритмы</a>).
|
||||
|
||||
Тесты:
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
//// tab | Тест
|
||||
|
||||
Вчера мой друг написал: "Если вы написали incorrectly правильно, значит вы написали это неправильно". На что я ответил: "Верно, но 'incorrectly' — это неправильно, а не '"incorrectly"'".
|
||||
Вчера мой друг написал: "If you spell incorrectly correctly, you have spelled it incorrectly". To which I answered: "Correct, but 'incorrectly' is incorrectly not '"incorrectly"'".
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
@@ -197,10 +197,10 @@ works(foo="bar") # Это работает 🎉
|
||||
|
||||
### abbr даёт полную расшифровку { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done – Как привести дела в порядок">GTD</abbr>
|
||||
* <abbr title="less than – меньше чем"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token – XML веб‑токен">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface – Параллельный серверный интерфейс шлюза">PSGI</abbr>
|
||||
* <abbr title="Getting Things Done - Как привести дела в порядок">GTD</abbr>
|
||||
* <abbr title="less than - меньше чем"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token - XML веб‑токен">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Параллельный серверный интерфейс шлюза">PSGI</abbr>
|
||||
|
||||
### abbr даёт объяснение { #the-abbr-gives-an-explanation }
|
||||
|
||||
@@ -209,8 +209,8 @@ works(foo="bar") # Это работает 🎉
|
||||
|
||||
### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla: документация для разработчиков, созданная командой Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output – Ввод/Вывод: чтение или запись на диск, сетевое взаимодействие.">I/O</abbr>.
|
||||
* <abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla: документация для разработчиков, созданная командой Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output - Ввод/Вывод: чтение или запись на диск, сетевое взаимодействие.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
|
||||
### Использование имени функции-обработчика пути как operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
### Использование имени *функции-обработчика пути* как operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
Если вы хотите использовать имена функций ваших API в качестве `operationId`, вы можете пройти по всем из них и переопределить `operation_id` каждой *операции пути* с помощью их `APIRoute.name`.
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
## Исключить из OpenAPI { #exclude-from-openapi }
|
||||
|
||||
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматической документации), используйте параметр `include_in_schema` и установите его в `False`:
|
||||
Чтобы исключить *операцию пути* из генерируемой схемы OpenAPI (а значит, и из автоматических систем документации), используйте параметр `include_in_schema` и установите его в `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
|
||||
Вы можете ограничить количество строк из docstring *функции-обработчика пути*, используемых для OpenAPI.
|
||||
|
||||
Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать текст, используемый для OpenAPI, в этой точке.
|
||||
Добавление `\f` (экранированного символа «form feed») заставит **FastAPI** обрезать вывод, используемый для OpenAPI, в этой точке.
|
||||
|
||||
Эта часть не попадёт в документацию, но другие инструменты (например, Sphinx) смогут использовать остальное.
|
||||
Это не отобразится в документации, но другие инструменты (например, Sphinx) смогут использовать остальное.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
Вы, вероятно, уже видели, как объявлять `response_model` и `status_code` для *операции пути*.
|
||||
|
||||
Это определяет метаданные об основном ответе *операции пути*.
|
||||
Это определяет метаданные об основном HTTP-ответе *операции пути*.
|
||||
|
||||
Также можно объявлять дополнительные ответы с их моделями, статус-кодами и т.д.
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
Там есть `tags`, `parameters`, `requestBody`, `responses` и т.д.
|
||||
|
||||
Эта спецификация OpenAPI, специфичная для *операции пути*, обычно генерируется автоматически **FastAPI**, но вы также можете её расширить.
|
||||
Эта специфичная для *операции пути* схема OpenAPI обычно генерируется автоматически **FastAPI**, но вы также можете её расширить.
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
@@ -129,19 +129,19 @@
|
||||
}
|
||||
```
|
||||
|
||||
### Пользовательская схема OpenAPI для операции пути { #custom-openapi-path-operation-schema }
|
||||
### Пользовательская схема OpenAPI для *операции пути* { #custom-openapi-path-operation-schema }
|
||||
|
||||
Словарь в `openapi_extra` будет объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*.
|
||||
Словарь в `openapi_extra` будет глубоко объединён с автоматически сгенерированной схемой OpenAPI для *операции пути*.
|
||||
|
||||
Таким образом, вы можете добавить дополнительные данные к автоматически сгенерированной схеме.
|
||||
|
||||
Например, вы можете решить читать и валидировать запрос своим кодом, не используя автоматические возможности FastAPI и Pydantic, но при этом захотите описать запрос в схеме OpenAPI.
|
||||
Например, вы можете решить читать и валидировать HTTP-запрос своим кодом, не используя автоматические возможности FastAPI с Pydantic, но при этом захотите описать HTTP-запрос в схеме OpenAPI.
|
||||
|
||||
Это можно сделать с помощью `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
|
||||
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <abbr title="преобразовано из простого формата, например байтов, в объекты Python">распарсено</abbr> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
|
||||
В этом примере мы не объявляли никакую Pydantic-модель. Фактически тело запроса даже не <abbr title="converted from some plain format, like bytes, into Python objects - преобразовано из простого формата, например байтов, в объекты Python">распарсено</abbr> как JSON, оно читается напрямую как `bytes`, а функция `magic_data_reader()` будет отвечать за его парсинг каким-то способом.
|
||||
|
||||
Тем не менее, мы можем объявить ожидаемую схему для тела запроса.
|
||||
|
||||
@@ -149,52 +149,20 @@
|
||||
|
||||
Используя тот же приём, вы можете воспользоваться Pydantic-моделью, чтобы определить JSON Schema, которая затем будет включена в пользовательский раздел схемы OpenAPI для *операции пути*.
|
||||
|
||||
И вы можете сделать это, даже если тип данных в запросе — не JSON.
|
||||
И вы можете сделать это, даже если тип данных в HTTP-запросе — не JSON.
|
||||
|
||||
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого запроса как YAML, а не JSON:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
Например, в этом приложении мы не используем встроенную функциональность FastAPI для извлечения JSON Schema из моделей Pydantic, равно как и автоматическую валидацию JSON. Мы объявляем тип содержимого HTTP-запроса как YAML, а не JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py hl[15:20, 22] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic версии 1 метод для получения JSON Schema модели назывался `Item.schema()`, в Pydantic версии 2 метод называется `Item.model_json_schema()`.
|
||||
|
||||
///
|
||||
|
||||
Тем не менее, хотя мы не используем встроенную функциональность по умолчанию, мы всё равно используем Pydantic-модель, чтобы вручную сгенерировать JSON Schema для данных, которые мы хотим получить в YAML.
|
||||
|
||||
Затем мы работаем с запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку запроса как JSON.
|
||||
Затем мы работаем с HTTP-запросом напрямую и извлекаем тело как `bytes`. Это означает, что FastAPI даже не попытается распарсить полезную нагрузку HTTP-запроса как JSON.
|
||||
|
||||
А затем в нашем коде мы напрямую парсим этот YAML и снова используем ту же Pydantic-модель для валидации YAML-содержимого:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
А затем в нашем коде мы напрямую парсим это содержимое YAML и снова используем ту же Pydantic-модель, чтобы валидировать YAML-содержимое:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py hl[24:31] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic версии 1 метод для парсинга и валидации объекта назывался `Item.parse_obj()`, в Pydantic версии 2 метод называется `Item.model_validate()`.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Здесь мы переиспользуем ту же Pydantic-модель.
|
||||
|
||||
@@ -46,12 +46,6 @@ $ pip install "fastapi[all]"
|
||||
|
||||
</div>
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic v1 он входил в основной пакет. Теперь он распространяется как отдельный пакет, чтобы вы могли установить его только при необходимости.
|
||||
|
||||
///
|
||||
|
||||
### Создание объекта `Settings` { #create-the-settings-object }
|
||||
|
||||
Импортируйте `BaseSettings` из Pydantic и создайте подкласс, очень похожий на Pydantic‑модель.
|
||||
@@ -60,24 +54,8 @@ $ pip install "fastapi[all]"
|
||||
|
||||
Вы можете использовать все те же возможности валидации и инструменты, что и для Pydantic‑моделей, например разные типы данных и дополнительную валидацию через `Field()`.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic v1 вы бы импортировали `BaseSettings` напрямую из `pydantic`, а не из `pydantic_settings`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_pv1_py39.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Если вам нужно что-то быстро скопировать и вставить, не используйте этот пример — воспользуйтесь последним ниже.
|
||||
@@ -215,8 +193,6 @@ APP_NAME="ChimichangApp"
|
||||
|
||||
Затем обновите ваш `config.py` так:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
|
||||
/// tip | Совет
|
||||
@@ -225,26 +201,6 @@ APP_NAME="ChimichangApp"
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config_pv1.py hl[9:10] *}
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Класс `Config` используется только для конфигурации Pydantic. Подробнее см. <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic версии 1 конфигурация задавалась во внутреннем классе `Config`, в Pydantic версии 2 — в атрибуте `model_config`. Этот атрибут принимает `dict`, и чтобы получить автозавершение и ошибки «на лету», вы можете импортировать и использовать `SettingsConfigDict` для описания этого `dict`.
|
||||
|
||||
///
|
||||
|
||||
Здесь мы задаем параметр конфигурации `env_file` внутри вашего класса Pydantic `Settings` и устанавливаем значение равным имени файла dotenv, который хотим использовать.
|
||||
|
||||
### Создание `Settings` только один раз с помощью `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
|
||||
Если у вас старое приложение FastAPI, возможно, вы используете Pydantic версии 1.
|
||||
|
||||
FastAPI поддерживает и Pydantic v1, и v2 начиная с версии 0.100.0.
|
||||
FastAPI версии 0.100.0 поддерживал либо Pydantic v1, либо v2. Он использовал ту версию, которая была установлена.
|
||||
|
||||
Если у вас был установлен Pydantic v2, использовался он. Если вместо этого был установлен Pydantic v1 — использовался он.
|
||||
FastAPI версии 0.119.0 добавил частичную поддержку Pydantic v1 изнутри Pydantic v2 (как `pydantic.v1`), чтобы упростить миграцию на v2.
|
||||
|
||||
Сейчас Pydantic v1 объявлен устаревшим, и поддержка его будет удалена в следующих версиях FastAPI, поэтому вам следует **перейти на Pydantic v2**. Так вы получите последние возможности, улучшения и исправления.
|
||||
FastAPI 0.126.0 убрал поддержку Pydantic v1, при этом ещё некоторое время продолжал поддерживать `pydantic.v1`.
|
||||
|
||||
/// warning | Предупреждение
|
||||
|
||||
Кроме того, команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**.
|
||||
Команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с **Python 3.14**.
|
||||
|
||||
Это включает `pydantic.v1`, который больше не поддерживается в Python 3.14 и выше.
|
||||
|
||||
Если вы хотите использовать последние возможности Python, вам нужно убедиться, что вы используете Pydantic v2.
|
||||
|
||||
///
|
||||
|
||||
Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и **новые возможности в FastAPI 0.119.0**, которые помогут выполнить постепенную миграцию.
|
||||
Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и **возможности FastAPI 0.119.0**, которые помогут выполнить постепенную миграцию.
|
||||
|
||||
## Официальное руководство { #official-guide }
|
||||
|
||||
@@ -38,13 +40,13 @@ FastAPI поддерживает и Pydantic v1, и v2 начиная с вер
|
||||
|
||||
Вы можете использовать <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> от той же команды Pydantic.
|
||||
|
||||
Этот инструмент поможет автоматически внести большую часть необходимых изменений в код.
|
||||
Этот инструмент поможет автоматически изменить большую часть кода, который нужно изменить.
|
||||
|
||||
После этого запустите тесты и проверьте, что всё работает. Если да — на этом всё. 😎
|
||||
После этого вы можете запустить тесты и проверить, что всё работает. Если да — на этом всё. 😎
|
||||
|
||||
## Pydantic v1 в v2 { #pydantic-v1-in-v2 }
|
||||
|
||||
Pydantic v2 включает всё из Pydantic v1 как подмодуль `pydantic.v1`.
|
||||
Pydantic v2 включает всё из Pydantic v1 как подмодуль `pydantic.v1`. Но это больше не поддерживается в версиях Python выше 3.13.
|
||||
|
||||
Это означает, что вы можете установить последнюю версию Pydantic v2 и импортировать и использовать старые компоненты Pydantic v1 из этого подмодуля так, как если бы у вас был установлен старый Pydantic v1.
|
||||
|
||||
@@ -52,7 +54,7 @@ Pydantic v2 включает всё из Pydantic v1 как подмодуль `
|
||||
|
||||
### Поддержка FastAPI для Pydantic v1 внутри v2 { #fastapi-support-for-pydantic-v1-in-v2 }
|
||||
|
||||
Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 в составе Pydantic v2, чтобы упростить миграцию на v2.
|
||||
Начиная с FastAPI 0.119.0, есть также частичная поддержка Pydantic v1 изнутри Pydantic v2, чтобы упростить миграцию на v2.
|
||||
|
||||
Таким образом, вы можете обновить Pydantic до последней версии 2 и сменить импорты на подмодуль `pydantic.v1` — во многих случаях всё просто заработает.
|
||||
|
||||
@@ -106,7 +108,7 @@ graph TB
|
||||
style V2Field fill:#f9fff3
|
||||
```
|
||||
|
||||
В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же операции пути (обработчике пути) вашего приложения FastAPI:
|
||||
В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же **операции пути** (обработчике пути) вашего приложения FastAPI:
|
||||
|
||||
{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}
|
||||
|
||||
@@ -122,12 +124,12 @@ graph TB
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Сначала попробуйте `bump-pydantic`. Если тесты проходят и всё работает, вы справились одной командой. ✨
|
||||
Сначала попробуйте `bump-pydantic`: если тесты проходят и всё работает, вы справились одной командой. ✨
|
||||
|
||||
///
|
||||
|
||||
Если `bump-pydantic` не подходит для вашего случая, вы можете использовать поддержку одновременной работы моделей Pydantic v1 и v2 в одном приложении, чтобы мигрировать на Pydantic v2 постепенно.
|
||||
|
||||
Сначала обновите Pydantic до последней 2-й версии и измените импорты так, чтобы все ваши модели использовали `pydantic.v1`.
|
||||
Сначала вы можете обновить Pydantic до последней 2-й версии и изменить импорты так, чтобы все ваши модели использовали `pydantic.v1`.
|
||||
|
||||
Затем начните мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶
|
||||
Затем вы можете начать мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Разделять схемы OpenAPI для входа и выхода или нет { #separate-openapi-schemas-for-input-and-output-or-not }
|
||||
|
||||
При использовании **Pydantic v2** сгенерированный OpenAPI становится чуть более точным и **корректным**, чем раньше. 😎
|
||||
С момента выхода **Pydantic v2** сгенерированный OpenAPI стал чуть более точным и **корректным**, чем раньше. 😎
|
||||
|
||||
На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON схемы** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
|
||||
На самом деле, в некоторых случаях в OpenAPI будет даже **две JSON Schema** для одной и той же Pydantic‑модели: для входа и для выхода — в зависимости от наличия **значений по умолчанию**.
|
||||
|
||||
Посмотрим, как это работает, и как это изменить при необходимости.
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *}
|
||||
|
||||
…то, поскольку у `description` есть значение по умолчанию, даже если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**.
|
||||
…то, поскольку у `description` есть значение по умолчанию, если вы **ничего не вернёте** для этого поля, оно всё равно будет иметь это **значение по умолчанию**.
|
||||
|
||||
### Модель для данных ответа { #model-for-output-response-data }
|
||||
|
||||
@@ -46,13 +46,13 @@
|
||||
|
||||
Это означает, что у него **всегда будет какое‑то значение**, просто иногда это значение может быть `None` (или `null` в JSON).
|
||||
|
||||
Следовательно, клиентам, использующим ваш API, не нужно проверять наличие этого значения: они могут **исходить из того, что поле всегда присутствует**, а в некоторых случаях имеет значение по умолчанию `None`.
|
||||
Это означает, что клиентам, использующим ваш API, не нужно проверять, существует ли это значение или нет: они могут **исходить из того, что поле всегда присутствует**, но в некоторых случаях оно будет иметь значение по умолчанию `None`.
|
||||
|
||||
В OpenAPI это описывается тем, что поле помечается как **обязательное**, поскольку оно всегда присутствует.
|
||||
|
||||
Из‑за этого JSON Schema для модели может отличаться в зависимости от использования для **входа** или **выхода**:
|
||||
|
||||
* для **входа** `description` не будет обязательным
|
||||
* для **входа** `description` **не будет обязательным**
|
||||
* для **выхода** оно будет **обязательным** (и при этом может быть `None`, или, в терминах JSON, `null`)
|
||||
|
||||
### Выходная модель в документации { #model-for-output-in-docs }
|
||||
@@ -79,11 +79,11 @@
|
||||
|
||||
## Не разделять схемы { #do-not-separate-schemas }
|
||||
|
||||
Однако бывают случаи, когда вы хотите иметь **одну и ту же схему для входа и выхода**.
|
||||
Теперь бывают случаи, когда вы хотите иметь **одну и ту же схему для входа и выхода**.
|
||||
|
||||
Главный сценарий — когда у вас уже есть сгенерированный клиентский код/SDK, и вы пока не хотите обновлять весь этот автогенерируемый код/SDK (рано или поздно вы это сделаете, но не сейчас).
|
||||
Главный сценарий — когда у вас уже есть сгенерированный клиентский код/SDK, и вы пока не хотите обновлять весь этот автогенерируемый клиентский код/SDK, вероятно, вы захотите сделать это в какой-то момент, но, возможно, не прямо сейчас.
|
||||
|
||||
В таком случае вы можете отключить эту функциональность в FastAPI с помощью параметра `separate_input_output_schemas=False`.
|
||||
В таком случае вы можете отключить эту функциональность в **FastAPI** с помощью параметра `separate_input_output_schemas=False`.
|
||||
|
||||
/// info | Информация
|
||||
|
||||
@@ -95,10 +95,8 @@
|
||||
|
||||
### Одна и та же схема для входной и выходной моделей в документации { #same-schema-for-input-and-output-models-in-docs }
|
||||
|
||||
Теперь для этой модели будет одна общая схема и для входа, и для выхода — только `Item`, и в ней `description` будет **не обязательным**:
|
||||
И теперь для модели будет одна общая схема и для входа, и для выхода — только `Item`, и в ней `description` будет **не обязательным**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
|
||||
</div>
|
||||
|
||||
Это то же поведение, что и в Pydantic v1. 🤓
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
</style>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastapi.tiangolo.com"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
<a href="https://fastapi.tiangolo.com/ru"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" alt="FastAPI"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<em>Фреймворк FastAPI: высокая производительность, прост в изучении, быстрый в разработке, готов к продакшн</em>
|
||||
<em>Фреймворк FastAPI: высокая производительность, прост в изучении, быстро писать код, готов к продакшн</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/fastapi/fastapi/actions?query=workflow%3ATest+event%3Apush+branch%3Amaster" target="_blank">
|
||||
@@ -40,7 +40,7 @@ FastAPI — это современный, быстрый (высокопрои
|
||||
* **Скорость**: Очень высокая производительность, на уровне **NodeJS** и **Go** (благодаря Starlette и Pydantic). [Один из самых быстрых доступных фреймворков Python](#performance).
|
||||
* **Быстрота разработки**: Увеличьте скорость разработки фич примерно на 200–300%. *
|
||||
* **Меньше ошибок**: Сократите примерно на 40% количество ошибок, вызванных человеком (разработчиком). *
|
||||
* **Интуитивность**: Отличная поддержка редактора кода. <abbr title="также известное как: автодополнение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
|
||||
* **Интуитивность**: Отличная поддержка редактора кода. <abbr title="also known as auto-complete, autocompletion, IntelliSense - также известное как: автодополнение, автозавершение, IntelliSense">Автозавершение</abbr> везде. Меньше времени на отладку.
|
||||
* **Простота**: Разработан так, чтобы его было легко использовать и осваивать. Меньше времени на чтение документации.
|
||||
* **Краткость**: Минимизируйте дублирование кода. Несколько возможностей из каждого объявления параметров. Меньше ошибок.
|
||||
* **Надежность**: Получите код, готовый к продакшн. С автоматической интерактивной документацией.
|
||||
@@ -117,6 +117,12 @@ FastAPI — это современный, быстрый (высокопрои
|
||||
|
||||
---
|
||||
|
||||
## Мини-документальный фильм о FastAPI { #fastapi-mini-documentary }
|
||||
|
||||
В конце 2025 года вышел <a href="https://www.youtube.com/watch?v=mpR8ngthqiE" class="external-link" target="_blank">мини-документальный фильм о FastAPI</a>, вы можете посмотреть его онлайн:
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=mpR8ngthqiE" target="_blank"><img src="https://fastapi.tiangolo.com/img/fastapi-documentary.jpg" alt="FastAPI Mini Documentary"></a>
|
||||
|
||||
## **Typer**, FastAPI для CLI { #typer-the-fastapi-of-clis }
|
||||
|
||||
<a href="https://typer.tiangolo.com" target="_blank"><img src="https://typer.tiangolo.com/img/logo-margin/logo-margin-vector.svg" style="width: 20%;"></a>
|
||||
@@ -257,7 +263,7 @@ INFO: Application startup complete.
|
||||
|
||||
* Получает HTTP-запросы по _путям_ `/` и `/items/{item_id}`.
|
||||
* Оба _пути_ используют `GET` <em>операции</em> (также известные как HTTP _методы_).
|
||||
* _Путь_ `/items/{item_id}` имеет _параметр пути_ `item_id`, который должен быть `int`.
|
||||
* _Путь_ `/items/{item_id}` имеет _path-параметр_ `item_id`, который должен быть `int`.
|
||||
* _Путь_ `/items/{item_id}` имеет необязательный `str` _параметр запроса_ `q`.
|
||||
|
||||
### Интерактивная документация API { #interactive-api-docs }
|
||||
@@ -278,9 +284,9 @@ INFO: Application startup complete.
|
||||
|
||||
## Пример обновления { #example-upgrade }
|
||||
|
||||
Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` запроса.
|
||||
Теперь измените файл `main.py`, чтобы принимать тело запроса из `PUT` HTTP-запроса.
|
||||
|
||||
Объявите тело, используя стандартные типы Python, спасибо Pydantic.
|
||||
Объявите тело запроса, используя стандартные типы Python, спасибо Pydantic.
|
||||
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Union
|
||||
@@ -318,7 +324,7 @@ def update_item(item_id: int, item: Item):
|
||||
|
||||
Перейдите на <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* Интерактивная документация API будет автоматически обновлена, включая новое тело:
|
||||
* Интерактивная документация API будет автоматически обновлена, включая новое тело запроса:
|
||||
|
||||

|
||||
|
||||
@@ -334,13 +340,13 @@ def update_item(item_id: int, item: Item):
|
||||
|
||||
Теперь откройте <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* Альтернативная документация также отразит новый параметр запроса и тело:
|
||||
* Альтернативная документация также отразит новый параметр запроса и тело запроса:
|
||||
|
||||

|
||||
|
||||
### Подведём итоги { #recap }
|
||||
|
||||
Итак, вы объявляете **один раз** типы параметров, тела запроса и т.д. как параметры функции.
|
||||
Итак, вы объявляете **один раз** типы параметров, тело запроса и т.д. как параметры функции.
|
||||
|
||||
Вы делаете это с помощью стандартных современных типов Python.
|
||||
|
||||
@@ -390,13 +396,13 @@ item: Item
|
||||
|
||||
Возвращаясь к предыдущему примеру кода, **FastAPI** будет:
|
||||
|
||||
* Валидировать наличие `item_id` в пути для `GET` и `PUT` запросов.
|
||||
* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` запросов.
|
||||
* Валидировать наличие `item_id` в пути для `GET` и `PUT` HTTP-запросов.
|
||||
* Валидировать, что `item_id` имеет тип `int` для `GET` и `PUT` HTTP-запросов.
|
||||
* Если это не так, клиент увидит полезную понятную ошибку.
|
||||
* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` запросов.
|
||||
* Проверять, есть ли необязательный параметр запроса с именем `q` (например, `http://127.0.0.1:8000/items/foo?q=somequery`) для `GET` HTTP-запросов.
|
||||
* Поскольку параметр `q` объявлен с `= None`, он необязателен.
|
||||
* Без `None` он был бы обязательным (как тело запроса в случае с `PUT`).
|
||||
* Для `PUT` запросов к `/items/{item_id}` читать тело запроса как JSON:
|
||||
* Для `PUT` HTTP-запросов к `/items/{item_id}` читать тело запроса как JSON:
|
||||
* Проверять, что есть обязательный атрибут `name`, который должен быть `str`.
|
||||
* Проверять, что есть обязательный атрибут `price`, который должен быть `float`.
|
||||
* Проверять, что есть необязательный атрибут `is_offer`, который должен быть `bool`, если он присутствует.
|
||||
@@ -433,13 +439,13 @@ item: Item
|
||||
|
||||

|
||||
|
||||
Более полный пример с дополнительными возможностями см. в <a href="https://fastapi.tiangolo.com/ru/tutorial/">Учебник - Руководство пользователя</a>.
|
||||
Более полный пример с дополнительными возможностями см. в <a href="https://fastapi.tiangolo.com/ru/tutorial/">Руководство - Руководство пользователя</a>.
|
||||
|
||||
**Осторожно, спойлер**: учебник - руководство включает:
|
||||
**Осторожно, спойлер**: руководство - руководство пользователя включает:
|
||||
|
||||
* Объявление **параметров** из других источников: **HTTP-заголовки**, **cookies**, **поля формы** и **файлы**.
|
||||
* Как задать **ограничения валидации** вроде `maximum_length` или `regex`.
|
||||
* Очень мощную и простую в использовании систему **<abbr title="также известная как: компоненты, ресурсы, провайдеры, сервисы, инъекции">внедрения зависимостей</abbr>**.
|
||||
* Очень мощную и простую в использовании систему **<abbr title="also known as components, resources, providers, services, injectables - внедрение зависимостей: компоненты, ресурсы, провайдеры, сервисы, внедряемые сущности">Dependency Injection</abbr>**.
|
||||
* Безопасность и аутентификацию, включая поддержку **OAuth2** с **JWT токенами** и **HTTP Basic** аутентификацию.
|
||||
* Более продвинутые (но столь же простые) приёмы объявления **глубоко вложенных JSON-моделей** (спасибо Pydantic).
|
||||
* Интеграцию **GraphQL** с <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> и другими библиотеками.
|
||||
@@ -490,7 +496,7 @@ Deploying to FastAPI Cloud...
|
||||
|
||||
**<a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>** создан тем же автором и командой, что и **FastAPI**.
|
||||
|
||||
Он упрощает процесс **создания образа**, **развертывания** и **доступа** к API при минимальных усилиях.
|
||||
Он упрощает процесс **создания**, **развертывания** и **доступа** к API при минимальных усилиях.
|
||||
|
||||
Он переносит тот же **опыт разработчика**, что и при создании приложений на FastAPI, на их **развертывание** в облаке. 🎉
|
||||
|
||||
@@ -524,11 +530,11 @@ FastAPI зависит от Pydantic и Starlette.
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> — обязателен, если вы хотите использовать `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> — обязателен, если вы хотите использовать конфигурацию шаблонов по умолчанию.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> — обязателен, если вы хотите поддерживать <abbr title="преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</abbr> форм через `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> — обязателен, если вы хотите поддерживать <abbr title="converting the string that comes from an HTTP request into Python data - преобразование строки, полученной из HTTP-запроса, в данные Python">«парсинг»</abbr> форм через `request.form()`.
|
||||
|
||||
Используется FastAPI:
|
||||
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> — сервер, который загружает и обслуживает ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
|
||||
* <a href="https://www.uvicorn.dev" target="_blank"><code>uvicorn</code></a> — сервер, который загружает и «отдаёт» ваше приложение. Включает `uvicorn[standard]`, содержащий некоторые зависимости (например, `uvloop`), нужные для высокой производительности.
|
||||
* `fastapi-cli[standard]` — чтобы предоставить команду `fastapi`.
|
||||
* Включает `fastapi-cloud-cli`, который позволяет развернуть ваше приложение FastAPI в <a href="https://fastapicloud.com" class="external-link" target="_blank">FastAPI Cloud</a>.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Большие приложения, в которых много файлов { #bigger-applications-multiple-files }
|
||||
# Большие приложения — несколько файлов { #bigger-applications-multiple-files }
|
||||
|
||||
При построении приложения или веб-API нам редко удается поместить всё в один файл.
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Обратите внимание, что в каждом каталоге и подкаталоге имеется файл `__init__.py`
|
||||
Есть несколько файлов `__init__.py`: по одному в каждом каталоге или подкаталоге.
|
||||
|
||||
Это как раз то, что позволяет импортировать код из одного файла в другой.
|
||||
|
||||
@@ -43,61 +43,63 @@ from app.routers import items
|
||||
|
||||
///
|
||||
|
||||
* Всё помещается в каталоге `app`. В нём также находится пустой файл `app/__init__.py`. Таким образом, `app` является "Python-пакетом" (коллекцией модулей Python).
|
||||
* Он содержит файл `app/main.py`. Данный файл является частью пакета (т.е. находится внутри каталога, содержащего файл `__init__.py`), и, соответственно, он является модулем пакета: `app.main`.
|
||||
* Всё помещается в каталоге `app`. В нём также находится пустой файл `app/__init__.py`. Таким образом, `app` является "Python-пакетом" (коллекцией "Python-модулей"): `app`.
|
||||
* Он содержит файл `app/main.py`. Данный файл является частью Python-пакета (т.е. находится внутри каталога, содержащего файл `__init__.py`), и, соответственно, он является модулем этого пакета: `app.main`.
|
||||
* Он также содержит файл `app/dependencies.py`, который также, как и `app/main.py`, является модулем: `app.dependencies`.
|
||||
* Здесь также находится подкаталог `app/routers/`, содержащий `__init__.py`. Он является суб-пакетом: `app.routers`.
|
||||
* Файл `app/routers/items.py` находится внутри пакета `app/routers/`. Таким образом, он является суб-модулем: `app.routers.items`.
|
||||
* Точно также `app/routers/users.py` является ещё одним суб-модулем: `app.routers.users`.
|
||||
* Подкаталог `app/internal/`, содержащий файл `__init__.py`, является ещё одним суб-пакетом: `app.internal`.
|
||||
* А файл `app/internal/admin.py` является ещё одним суб-модулем: `app.internal.admin`.
|
||||
* Здесь также находится подкаталог `app/routers/`, содержащий `__init__.py`. Он является Python-субпакетом: `app.routers`.
|
||||
* Файл `app/routers/items.py` находится внутри пакета `app/routers/`. Таким образом, он является субмодулем: `app.routers.items`.
|
||||
* Точно так же `app/routers/users.py` является ещё одним субмодулем: `app.routers.users`.
|
||||
* Подкаталог `app/internal/`, содержащий файл `__init__.py`, является ещё одним Python-субпакетом: `app.internal`.
|
||||
* А файл `app/internal/admin.py` является ещё одним субмодулем: `app.internal.admin`.
|
||||
|
||||
<img src="/img/tutorial/bigger-applications/package.drawio.svg">
|
||||
|
||||
Та же самая файловая структура приложения, но с комментариями:
|
||||
|
||||
```
|
||||
```bash
|
||||
.
|
||||
├── app # "app" пакет
|
||||
│ ├── __init__.py # этот файл превращает "app" в "Python-пакет"
|
||||
│ ├── main.py # модуль "main", напр.: import app.main
|
||||
│ ├── dependencies.py # модуль "dependencies", напр.: import app.dependencies
|
||||
│ └── routers # суб-пакет "routers"
|
||||
│ │ ├── __init__.py # превращает "routers" в суб-пакет
|
||||
│ │ ├── items.py # суб-модуль "items", напр.: import app.routers.items
|
||||
│ │ └── users.py # суб-модуль "users", напр.: import app.routers.users
|
||||
│ └── internal # суб-пакет "internal"
|
||||
│ ├── __init__.py # превращает "internal" в суб-пакет
|
||||
│ └── admin.py # суб-модуль "admin", напр.: import app.internal.admin
|
||||
├── app # "app" is a Python package
|
||||
│ ├── __init__.py # this file makes "app" a "Python package"
|
||||
│ ├── main.py # "main" module, e.g. import app.main
|
||||
│ ├── dependencies.py # "dependencies" module, e.g. import app.dependencies
|
||||
│ └── routers # "routers" is a "Python subpackage"
|
||||
│ │ ├── __init__.py # makes "routers" a "Python subpackage"
|
||||
│ │ ├── items.py # "items" submodule, e.g. import app.routers.items
|
||||
│ │ └── users.py # "users" submodule, e.g. import app.routers.users
|
||||
│ └── internal # "internal" is a "Python subpackage"
|
||||
│ ├── __init__.py # makes "internal" a "Python subpackage"
|
||||
│ └── admin.py # "admin" submodule, e.g. import app.internal.admin
|
||||
```
|
||||
|
||||
## `APIRouter` { #apirouter }
|
||||
|
||||
Давайте предположим, что для работы с пользователями используется отдельный файл (суб-модуль) `/app/routers/users.py`.
|
||||
Давайте предположим, что для работы с пользователями используется отдельный файл (субмодуль) `/app/routers/users.py`.
|
||||
|
||||
Для лучшей организации приложения, вы хотите отделить операции пути, связанные с пользователями, от остального кода.
|
||||
Вы хотите отделить *операции пути*, связанные с пользователями, от остального кода, чтобы сохранить порядок.
|
||||
|
||||
Но так, чтобы эти операции по-прежнему оставались частью **FastAPI** приложения/веб-API (частью одного пакета)
|
||||
Но это всё равно часть того же приложения/веб-API на **FastAPI** (часть того же «Python-пакета»).
|
||||
|
||||
С помощью `APIRouter` вы можете создать *операции пути* (*эндпоинты*) для данного модуля.
|
||||
С помощью `APIRouter` вы можете создать *операции пути* для этого модуля.
|
||||
|
||||
### Импорт `APIRouter` { #import-apirouter }
|
||||
|
||||
Точно также, как и в случае с классом `FastAPI`, вам нужно импортировать и создать объект класса `APIRouter`.
|
||||
Точно так же, как и в случае с классом `FastAPI`, вам нужно импортировать и создать его «экземпляр»:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### Создание *эндпоинтов* с помощью `APIRouter` { #path-operations-with-apirouter }
|
||||
### *Операции пути* с `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
В дальнейшем используйте `APIRouter` для объявления *эндпоинтов*, точно также, как вы используете класс `FastAPI`:
|
||||
И затем вы используете его, чтобы объявить ваши *операции пути*.
|
||||
|
||||
Используйте его так же, как вы использовали бы класс `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
Вы можете думать об `APIRouter` как об "уменьшенной версии" класса FastAPI`.
|
||||
Вы можете думать об `APIRouter` как об «мини-классе `FastAPI`».
|
||||
|
||||
`APIRouter` поддерживает все те же самые опции.
|
||||
Поддерживаются все те же опции.
|
||||
|
||||
`APIRouter` поддерживает все те же самые параметры, такие как `parameters`, `responses`, `dependencies`, `tags`, и т. д.
|
||||
Все те же `parameters`, `responses`, `dependencies`, `tags` и т.д.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -105,21 +107,21 @@ from app.routers import items
|
||||
|
||||
///
|
||||
|
||||
Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и создадим ещё один модуль с `APIRouter`.
|
||||
Мы собираемся подключить данный `APIRouter` к нашему основному приложению на `FastAPI`, но сначала давайте проверим зависимости и ещё один `APIRouter`.
|
||||
|
||||
## Зависимости { #dependencies }
|
||||
|
||||
Нам понадобятся некоторые зависимости, которые мы будем использовать в разных местах нашего приложения.
|
||||
Мы видим, что нам понадобятся некоторые зависимости, которые будут использоваться в нескольких местах приложения.
|
||||
|
||||
Мы поместим их в отдельный модуль `dependencies` (`app/dependencies.py`).
|
||||
Поэтому мы поместим их в отдельный модуль `dependencies` (`app/dependencies.py`).
|
||||
|
||||
Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомизированный `X-Token` из заголовка:
|
||||
Теперь мы воспользуемся простой зависимостью, чтобы прочитать кастомный HTTP-заголовок `X-Token`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Для простоты мы воспользовались неким воображаемым заголовоком.
|
||||
Для простоты мы воспользовались выдуманным заголовком.
|
||||
|
||||
В реальных случаях для получения наилучших результатов используйте интегрированные [утилиты безопасности](security/index.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -127,30 +129,29 @@ from app.routers import items
|
||||
|
||||
## Ещё один модуль с `APIRouter` { #another-module-with-apirouter }
|
||||
|
||||
Давайте также предположим, что у вас есть *эндпоинты*, отвечающие за обработку "items", и они находятся в модуле `app/routers/items.py`.
|
||||
Давайте также предположим, что у вас есть эндпоинты, отвечающие за обработку «items» в вашем приложении, и они находятся в модуле `app/routers/items.py`.
|
||||
|
||||
У вас определены следующие *операции пути* (*эндпоинты*):
|
||||
У вас определены *операции пути* для:
|
||||
|
||||
* `/items/`
|
||||
* `/items/{item_id}`
|
||||
|
||||
Тут всё точно также, как и в ситуации с `app/routers/users.py`.
|
||||
Тут всё та же структура, как и в случае с `app/routers/users.py`.
|
||||
|
||||
Но теперь мы хотим поступить немного умнее и слегка упростить код.
|
||||
Но мы хотим поступить умнее и слегка упростить код.
|
||||
|
||||
Мы знаем, что все *эндпоинты* данного модуля имеют некоторые общие свойства:
|
||||
Мы знаем, что все *операции пути* этого модуля имеют одинаковые:
|
||||
|
||||
* Префикс пути: `/items`.
|
||||
* Теги: (один единственный тег: `items`).
|
||||
* Дополнительные ответы (responses)
|
||||
* Зависимости: использование созданной нами зависимости `X-token`
|
||||
* `prefix` пути: `/items`.
|
||||
* `tags`: (один единственный тег: `items`).
|
||||
* Дополнительные `responses`.
|
||||
* `dependencies`: всем им нужна та зависимость `X-Token`, которую мы создали.
|
||||
|
||||
Таким образом, вместо того чтобы добавлять все эти свойства в функцию каждого отдельного *эндпоинта*,
|
||||
мы добавим их в `APIRouter`.
|
||||
Таким образом, вместо того чтобы добавлять всё это в каждую *операцию пути*, мы можем добавить это в `APIRouter`.
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
Так как каждый *эндпоинт* начинается с символа `/`:
|
||||
Так как путь каждой *операции пути* должен начинаться с `/`, как здесь:
|
||||
|
||||
```Python hl_lines="1"
|
||||
@router.get("/{item_id}")
|
||||
@@ -162,73 +163,74 @@ async def read_item(item_id: str):
|
||||
|
||||
В нашем случае префиксом является `/items`.
|
||||
|
||||
Мы также можем добавить в наш маршрутизатор (router) список `тегов` (`tags`) и дополнительных `ответов` (`responses`), которые являются общими для каждого *эндпоинта*.
|
||||
Мы также можем добавить список `tags` и дополнительные `responses`, которые будут применяться ко всем *операциям пути*, включённым в этот маршрутизатор.
|
||||
|
||||
И ещё мы можем добавить в наш маршрутизатор список `зависимостей`, которые должны вызываться при каждом обращении к *эндпоинтам*.
|
||||
И ещё мы можем добавить список `dependencies`, которые будут добавлены ко всем *операциям пути* в маршрутизаторе и будут выполняться/разрешаться для каждого HTTP-запроса к ним.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Обратите внимание, что также, как и в случае с зависимостями в декораторах *эндпоинтов* ([зависимости в декораторах операций пути](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), никакого значения в *функцию эндпоинта* передано не будет.
|
||||
Обратите внимание, что так же, как и в случае с [зависимостями в декораторах *операций пути*](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, никакое значение не будет передано в вашу *функцию-обработчик пути*.
|
||||
|
||||
///
|
||||
|
||||
В результате мы получим следующие эндпоинты:
|
||||
В результате пути для items теперь такие:
|
||||
|
||||
* `/items/`
|
||||
* `/items/{item_id}`
|
||||
|
||||
...как мы и планировали.
|
||||
|
||||
* Они будут помечены тегами из заданного списка, в нашем случае это `"items"`.
|
||||
* Эти теги особенно полезны для системы автоматической интерактивной документации (с использованием OpenAPI).
|
||||
* Каждый из них будет включать предопределенные ответы `responses`.
|
||||
* Каждый *эндпоинт* будет иметь список зависимостей (`dependencies`), исполняемых перед вызовом *эндпоинта*.
|
||||
* Если вы определили зависимости в самой операции пути, **то она также будет выполнена**.
|
||||
* Сначала выполняются зависимости маршрутизатора, затем вызываются [зависимости в декораторе](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, и, наконец, обычные параметрические зависимости.
|
||||
* Вы также можете добавить [зависимости `Security` с `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
|
||||
* Они будут помечены списком тегов, содержащим одну строку `"items"`.
|
||||
* Эти «теги» особенно полезны для систем автоматической интерактивной документации (с использованием OpenAPI).
|
||||
* Все они будут включать предопределённые `responses`.
|
||||
* Все эти *операции пути* будут иметь список `dependencies`, вычисляемых/выполняемых перед ними.
|
||||
* Если вы также объявите зависимости в конкретной *операции пути*, **они тоже будут выполнены**.
|
||||
* Сначала выполняются зависимости маршрутизатора, затем [`dependencies` в декораторе](dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}, и затем обычные параметрические зависимости.
|
||||
* Вы также можете добавить [`Security`-зависимости с `scopes`](../advanced/security/oauth2-scopes.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Например, с помощью зависимостей в `APIRouter` мы можем потребовать аутентификации для доступа ко всей группе *эндпоинтов*, не указывая зависимости для каждой отдельной функции *эндпоинта*.
|
||||
Например, наличие `dependencies` в `APIRouter` можно использовать, чтобы требовать аутентификацию для целой группы *операций пути*. Даже если зависимости не добавляются по отдельности к каждой из них.
|
||||
|
||||
///
|
||||
|
||||
/// check | Заметка
|
||||
|
||||
Параметры `prefix`, `tags`, `responses` и `dependencies` относятся к функционалу **FastAPI**, помогающему избежать дублирования кода.
|
||||
Параметры `prefix`, `tags`, `responses` и `dependencies` — это (как и во многих других случаях) просто возможность **FastAPI**, помогающая избежать дублирования кода.
|
||||
|
||||
///
|
||||
|
||||
### Импорт зависимостей { #import-the-dependencies }
|
||||
|
||||
Наш код находится в модуле `app.routers.items` (файл `app/routers/items.py`).
|
||||
Этот код находится в модуле `app.routers.items`, в файле `app/routers/items.py`.
|
||||
|
||||
И нам нужно вызвать функцию зависимости из модуля `app.dependencies` (файл `app/dependencies.py`).
|
||||
И нам нужно получить функцию зависимости из модуля `app.dependencies`, файла `app/dependencies.py`.
|
||||
|
||||
Мы используем операцию относительного импорта `..` для импорта зависимости:
|
||||
Поэтому мы используем относительный импорт с `..` для зависимостей:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### Как работает относительный импорт? { #how-relative-imports-work }
|
||||
#### Как работает относительный импорт { #how-relative-imports-work }
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Если вы прекрасно знаете, как работает импорт в Python, то переходите к следующему разделу.
|
||||
Если вы прекрасно знаете, как работает импорт, переходите к следующему разделу ниже.
|
||||
|
||||
///
|
||||
|
||||
Одна точка `.`, как в данном примере:
|
||||
Одна точка `.`, как здесь:
|
||||
|
||||
```Python
|
||||
from .dependencies import get_token_header
|
||||
```
|
||||
|
||||
означает:
|
||||
|
||||
* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` расположен в каталоге `app/routers/`)...
|
||||
* ... найдите модуль `dependencies` (файл `app/routers/dependencies.py`)...
|
||||
* ... и импортируйте из него функцию `get_token_header`.
|
||||
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
|
||||
* найти модуль `dependencies` (воображаемый файл `app/routers/dependencies.py`)...
|
||||
* и импортировать из него функцию `get_token_header`.
|
||||
|
||||
К сожалению, такого файла не существует, и наши зависимости находятся в файле `app/dependencies.py`.
|
||||
Но такого файла не существует, наши зависимости находятся в файле `app/dependencies.py`.
|
||||
|
||||
Вспомните, как выглядит файловая структура нашего приложения:
|
||||
|
||||
@@ -236,7 +238,7 @@ from .dependencies import get_token_header
|
||||
|
||||
---
|
||||
|
||||
Две точки `..`, как в данном примере:
|
||||
Две точки `..`, как здесь:
|
||||
|
||||
```Python
|
||||
from ..dependencies import get_token_header
|
||||
@@ -244,12 +246,12 @@ from ..dependencies import get_token_header
|
||||
|
||||
означают:
|
||||
|
||||
* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` находится в каталоге `app/routers/`)...
|
||||
* ... перейдите в родительский пакет (каталог `app/`)...
|
||||
* ... найдите в нём модуль `dependencies` (файл `app/dependencies.py`)...
|
||||
* ... и импортируйте из него функцию `get_token_header`.
|
||||
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
|
||||
* перейти в родительский пакет (каталог `app/`)...
|
||||
* и там найти модуль `dependencies` (файл `app/dependencies.py`)...
|
||||
* и импортировать из него функцию `get_token_header`.
|
||||
|
||||
Это работает верно! 🎉
|
||||
Это работает корректно! 🎉
|
||||
|
||||
---
|
||||
|
||||
@@ -261,61 +263,61 @@ from ...dependencies import get_token_header
|
||||
|
||||
то это бы означало:
|
||||
|
||||
* Начните с пакета, в котором находится данный модуль (файл `app/routers/items.py` находится в каталоге `app/routers/`)...
|
||||
* ... перейдите в родительский пакет (каталог `app/`)...
|
||||
* ... затем перейдите в родительский пакет текущего пакета (такого пакета не существует, `app` находится на самом верхнем уровне 😱)...
|
||||
* ... найдите в нём модуль `dependencies` (файл `app/dependencies.py`)...
|
||||
* ... и импортируйте из него функцию `get_token_header`.
|
||||
* Начать в том же пакете, в котором находится этот модуль (файл `app/routers/items.py`) (каталог `app/routers/`)...
|
||||
* перейти в родительский пакет (каталог `app/`)...
|
||||
* затем перейти в родительский пакет этого пакета (родительского пакета нет, `app` — верхний уровень 😱)...
|
||||
* и там найти модуль `dependencies` (файл `app/dependencies.py`)...
|
||||
* и импортировать из него функцию `get_token_header`.
|
||||
|
||||
Это будет относиться к некоторому пакету, находящемуся на один уровень выше чем `app/` и содержащему свой собственный файл `__init__.py`. Но ничего такого у нас нет. Поэтому это приведет к ошибке в нашем примере. 🚨
|
||||
Это ссылалось бы на какой-то пакет выше `app/`, со своим файлом `__init__.py` и т.п. Но у нас такого нет. Поэтому это вызвало бы ошибку в нашем примере. 🚨
|
||||
|
||||
Теперь вы знаете, как работает импорт в Python, и сможете использовать относительное импортирование в своих собственных приложениях любого уровня сложности. 🤓
|
||||
Но теперь вы знаете, как это работает, так что можете использовать относительные импорты в своих приложениях, независимо от того, насколько они сложные. 🤓
|
||||
|
||||
### Добавление пользовательских тегов (`tags`), ответов (`responses`) и зависимостей (`dependencies`) { #add-some-custom-tags-responses-and-dependencies }
|
||||
### Добавление пользовательских `tags`, `responses` и `dependencies` { #add-some-custom-tags-responses-and-dependencies }
|
||||
|
||||
Мы не будем добавлять префикс `/items` и список тегов `tags=["items"]` для каждого *эндпоинта*, т.к. мы уже их добавили с помощью `APIRouter`.
|
||||
Мы не добавляем префикс `/items` и `tags=["items"]` к каждой *операции пути*, потому что мы добавили их в `APIRouter`.
|
||||
|
||||
Но помимо этого мы можем добавить новые теги для каждого отдельного *эндпоинта*, а также некоторые дополнительные ответы (`responses`), характерные для данного *эндпоинта*:
|
||||
Но мы всё равно можем добавить _ещё_ `tags`, которые будут применяться к конкретной *операции пути*, а также дополнительные `responses`, специфичные для этой *операции пути*:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Последний *эндпоинт* будет иметь следующую комбинацию тегов: `["items", "custom"]`.
|
||||
Эта последняя операция пути будет иметь комбинацию тегов: `["items", "custom"]`.
|
||||
|
||||
А также в его документации будут содержаться оба ответа: один для `404` и другой для `403`.
|
||||
И в документации у неё будут оба ответа: один для `404` и один для `403`.
|
||||
|
||||
///
|
||||
|
||||
## Модуль main в `FastAPI` { #the-main-fastapi }
|
||||
## Основной `FastAPI` { #the-main-fastapi }
|
||||
|
||||
Теперь давайте посмотрим на модуль `app/main.py`.
|
||||
|
||||
Именно сюда вы импортируете и именно здесь вы используете класс `FastAPI`.
|
||||
|
||||
Это основной файл вашего приложения, который объединяет всё в одно целое.
|
||||
Это основной файл вашего приложения, который связывает всё воедино.
|
||||
|
||||
И теперь, когда большая часть логики приложения разделена на отдельные модули, основной файл `app/main.py` будет достаточно простым.
|
||||
И так как большая часть вашей логики теперь будет находиться в отдельных специфичных модулях, основной файл будет довольно простым.
|
||||
|
||||
### Импорт `FastAPI` { #import-fastapi }
|
||||
|
||||
Вы импортируете и создаете класс `FastAPI` как обычно.
|
||||
Вы импортируете и создаёте класс `FastAPI` как обычно.
|
||||
|
||||
Мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого отдельного маршрутизатора:
|
||||
И мы даже можем объявить [глобальные зависимости](dependencies/global-dependencies.md){.internal-link target=_blank}, которые будут объединены с зависимостями для каждого `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Импорт `APIRouter` { #import-the-apirouter }
|
||||
|
||||
Теперь мы импортируем другие суб-модули, содержащие `APIRouter`:
|
||||
Теперь мы импортируем другие субмодули, содержащие `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются суб-модулями одного и того же Python-пакета `app`, то мы сможем их импортировать, воспользовавшись операцией относительного импорта `.`.
|
||||
Так как файлы `app/routers/users.py` и `app/routers/items.py` являются субмодулями, входящими в один и тот же Python-пакет `app`, мы можем использовать одну точку `.` для импорта через «относительные импорты».
|
||||
|
||||
### Как работает импорт? { #how-the-importing-works }
|
||||
### Как работает импорт { #how-the-importing-works }
|
||||
|
||||
Данная строка кода:
|
||||
Этот фрагмент:
|
||||
|
||||
```Python
|
||||
from .routers import items, users
|
||||
@@ -323,15 +325,15 @@ from .routers import items, users
|
||||
|
||||
означает:
|
||||
|
||||
* Начните с пакета, в котором содержится данный модуль (файл `app/main.py` содержится в каталоге `app/`)...
|
||||
* ... найдите суб-пакет `routers` (каталог `app/routers/`)...
|
||||
* ... и из него импортируйте суб-модули `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)...
|
||||
* Начать в том же пакете, в котором находится этот модуль (файл `app/main.py`) (каталог `app/`)...
|
||||
* найти субпакет `routers` (каталог `app/routers/`)...
|
||||
* и импортировать из него субмодуль `items` (файл `app/routers/items.py`) и `users` (файл `app/routers/users.py`)...
|
||||
|
||||
В модуле `items` содержится переменная `router` (`items.router`), та самая, которую мы создали в файле `app/routers/items.py`, она является объектом класса `APIRouter`.
|
||||
В модуле `items` будет переменная `router` (`items.router`). Это та же самая, которую мы создали в файле `app/routers/items.py`, это объект `APIRouter`.
|
||||
|
||||
И затем мы сделаем то же самое для модуля `users`.
|
||||
И затем мы делаем то же самое для модуля `users`.
|
||||
|
||||
Мы также могли бы импортировать и другим методом:
|
||||
Мы также могли бы импортировать их так:
|
||||
|
||||
```Python
|
||||
from app.routers import items, users
|
||||
@@ -339,44 +341,44 @@ from app.routers import items, users
|
||||
|
||||
/// info | Примечание
|
||||
|
||||
Первая версия является примером относительного импорта:
|
||||
Первая версия — это «относительный импорт»:
|
||||
|
||||
```Python
|
||||
from .routers import items, users
|
||||
```
|
||||
|
||||
Вторая версия является примером абсолютного импорта:
|
||||
Вторая версия — это «абсолютный импорт»:
|
||||
|
||||
```Python
|
||||
from app.routers import items, users
|
||||
```
|
||||
|
||||
Узнать больше о пакетах и модулях в Python вы можете из <a href="https://docs.python.org/3/tutorial/modules.html" class="external-link" target="_blank">официальной документации Python о модулях</a>
|
||||
Чтобы узнать больше о Python-пакетах и модулях, прочитайте <a href="https://docs.python.org/3/tutorial/modules.html" class="external-link" target="_blank">официальную документацию Python о модулях</a>.
|
||||
|
||||
///
|
||||
|
||||
### Избегайте конфликтов имен { #avoid-name-collisions }
|
||||
### Избегайте конфликтов имён { #avoid-name-collisions }
|
||||
|
||||
Вместо того чтобы импортировать только переменную `router`, мы импортируем непосредственно суб-модуль `items`.
|
||||
Мы импортируем субмодуль `items` напрямую, вместо того чтобы импортировать только его переменную `router`.
|
||||
|
||||
Мы делаем это потому, что у нас есть ещё одна переменная `router` в суб-модуле `users`.
|
||||
Это потому, что у нас также есть другая переменная с именем `router` в субмодуле `users`.
|
||||
|
||||
Если бы мы импортировали их одну за другой, как показано в примере:
|
||||
Если бы мы импортировали их одну за другой, как здесь:
|
||||
|
||||
```Python
|
||||
from .routers.items import router
|
||||
from .routers.users import router
|
||||
```
|
||||
|
||||
то переменная `router` из `users` переписал бы переменную `router` из `items`, и у нас не было бы возможности использовать их одновременно.
|
||||
то `router` из `users` перезаписал бы `router` из `items`, и мы не смогли бы использовать их одновременно.
|
||||
|
||||
Поэтому, для того чтобы использовать обе эти переменные в одном файле, мы импортировали соответствующие суб-модули:
|
||||
Поэтому, чтобы иметь возможность использовать обе в одном файле, мы импортируем субмодули напрямую:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Подключение маршрутизаторов (`APIRouter`) для `users` и для `items` { #include-the-apirouters-for-users-and-items }
|
||||
### Подключение `APIRouter` для `users` и `items` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Давайте подключим маршрутизаторы (`router`) из суб-модулей `users` и `items`:
|
||||
Теперь давайте подключим `router` из субмодулей `users` и `items`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
@@ -388,79 +390,78 @@ from .routers.users import router
|
||||
|
||||
///
|
||||
|
||||
С помощью `app.include_router()` мы можем добавить каждый из маршрутизаторов (`APIRouter`) в основное приложение `FastAPI`.
|
||||
С помощью `app.include_router()` мы можем добавить каждый `APIRouter` в основное приложение `FastAPI`.
|
||||
|
||||
Он подключит все маршруты заданного маршрутизатора к нашему приложению.
|
||||
Он включит все маршруты этого маршрутизатора как часть приложения.
|
||||
|
||||
/// note | Технические детали
|
||||
|
||||
Фактически, внутри он создаст все *операции пути* для каждой операции пути объявленной в `APIRouter`.
|
||||
Фактически, внутри он создаст *операцию пути* для каждой *операции пути*, объявленной в `APIRouter`.
|
||||
|
||||
И под капотом всё будет работать так, как будто бы мы имеем дело с одним файлом приложения.
|
||||
Так что под капотом всё будет работать так, как будто всё было одним приложением.
|
||||
|
||||
///
|
||||
|
||||
/// check | Заметка
|
||||
|
||||
При подключении маршрутизаторов не стоит беспокоиться о производительности.
|
||||
При подключении маршрутизаторов не нужно беспокоиться о производительности.
|
||||
|
||||
Операция подключения займёт микросекунды и понадобится только при запуске приложения.
|
||||
Это займёт микросекунды и произойдёт только при старте.
|
||||
|
||||
Таким образом, это не повлияет на производительность. ⚡
|
||||
Так что это не повлияет на производительность. ⚡
|
||||
|
||||
///
|
||||
|
||||
### Подключение `APIRouter` с пользовательскими префиксом (`prefix`), тегами (`tags`), ответами (`responses`), и зависимостями (`dependencies`) { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
|
||||
### Подключение `APIRouter` с пользовательскими `prefix`, `tags`, `responses` и `dependencies` { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies }
|
||||
|
||||
Теперь давайте представим, что ваша организация передала вам файл `app/internal/admin.py`.
|
||||
|
||||
Он содержит `APIRouter` с некоторыми *эндпоитами* администрирования, которые ваша организация использует для нескольких проектов.
|
||||
Он содержит `APIRouter` с некоторыми административными *операциями пути*, которые ваша организация использует в нескольких проектах.
|
||||
|
||||
В данном примере это сделать очень просто. Но давайте предположим, что поскольку файл используется для нескольких проектов,
|
||||
то мы не можем модифицировать его, добавляя префиксы (`prefix`), зависимости (`dependencies`), теги (`tags`), и т.д. непосредственно в `APIRouter`:
|
||||
Для этого примера всё будет очень просто. Но допустим, что поскольку он используется совместно с другими проектами в организации, мы не можем модифицировать его и добавить `prefix`, `dependencies`, `tags` и т.д. непосредственно в `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
Но, несмотря на это, мы хотим использовать кастомный префикс (`prefix`) для подключенного маршрутизатора (`APIRouter`), в результате чего, каждая *операция пути* будет начинаться с `/admin`. Также мы хотим защитить наш маршрутизатор с помощью зависимостей, созданных для нашего проекта. И ещё мы хотим включить теги (`tags`) и ответы (`responses`).
|
||||
Но мы всё равно хотим задать пользовательский `prefix` при подключении `APIRouter`, чтобы все его *операции пути* начинались с `/admin`, хотим защитить его с помощью `dependencies`, которые у нас уже есть для этого проекта, и хотим включить `tags` и `responses`.
|
||||
|
||||
Мы можем применить все вышеперечисленные настройки, не изменяя начальный `APIRouter`. Нам всего лишь нужно передать нужные параметры в `app.include_router()`.
|
||||
Мы можем объявить всё это, не изменяя исходный `APIRouter`, передав эти параметры в `app.include_router()`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
Таким образом, оригинальный `APIRouter` не будет модифицирован, и мы сможем использовать файл `app/internal/admin.py` сразу в нескольких проектах организации.
|
||||
Таким образом исходный `APIRouter` останется неизменённым, и мы сможем продолжать делиться этим же файлом `app/internal/admin.py` с другими проектами в организации.
|
||||
|
||||
В результате, в нашем приложении каждый *эндпоинт* модуля `admin` будет иметь:
|
||||
В результате в нашем приложении каждая из *операций пути* из модуля `admin` будет иметь:
|
||||
|
||||
* Префикс `/admin`.
|
||||
* Тег `admin`.
|
||||
* Зависимость `get_token_header`.
|
||||
* Ответ `418`. 🍵
|
||||
|
||||
Это будет иметь место исключительно для `APIRouter` в нашем приложении, и не затронет любой другой код, использующий его.
|
||||
Но это повлияет только на этот `APIRouter` в нашем приложении, а не на любой другой код, который его использует.
|
||||
|
||||
Например, другие проекты, могут использовать тот же самый `APIRouter` с другими методами аутентификации.
|
||||
Так что, например, другие проекты могут использовать тот же `APIRouter` с другим методом аутентификации.
|
||||
|
||||
### Подключение отдельного *эндпоинта* { #include-a-path-operation }
|
||||
### Подключение *операции пути* { #include-a-path-operation }
|
||||
|
||||
Мы также можем добавить *эндпоинт* непосредственно в основное приложение `FastAPI`.
|
||||
Мы также можем добавлять *операции пути* напрямую в приложение `FastAPI`.
|
||||
|
||||
Здесь мы это делаем ... просто, чтобы показать, что это возможно 🤷:
|
||||
Здесь мы делаем это... просто чтобы показать, что можем 🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
и это будет работать корректно вместе с другими *эндпоинтами*, добавленными с помощью `app.include_router()`.
|
||||
и это будет работать корректно вместе со всеми другими *операциями пути*, добавленными через `app.include_router()`.
|
||||
|
||||
/// info | Сложные технические детали
|
||||
/// info | Очень технические детали
|
||||
|
||||
**Примечание**: это сложная техническая деталь, которую, скорее всего, **вы можете пропустить**.
|
||||
**Примечание**: это очень техническая деталь, которую, вероятно, можно **просто пропустить**.
|
||||
|
||||
---
|
||||
|
||||
Маршрутизаторы (`APIRouter`) не "монтируются" по-отдельности и не изолируются от остального приложения.
|
||||
`APIRouter` не «монтируются», они не изолированы от остального приложения.
|
||||
|
||||
Это происходит потому, что нужно включить их *эндпоинты* в OpenAPI схему и в интерфейс пользователя.
|
||||
Это потому, что мы хотим включить их *операции пути* в OpenAPI-схему и пользовательские интерфейсы.
|
||||
|
||||
В силу того, что мы не можем их изолировать и "примонтировать" независимо от остальных, *эндпоинты* клонируются (пересоздаются) и не подключаются напрямую.
|
||||
Так как мы не можем просто изолировать их и «смонтировать» независимо от остального, *операции пути* «клонируются» (пересоздаются), а не включаются напрямую.
|
||||
|
||||
///
|
||||
|
||||
@@ -478,26 +479,26 @@ $ fastapi dev app/main.py
|
||||
|
||||
</div>
|
||||
|
||||
Откройте документацию по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
И откройте документацию по адресу <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Вы увидите автоматическую API документацию. Она включает в себя маршруты из суб-модулей, используя верные маршруты, префиксы и теги:
|
||||
Вы увидите автоматическую документацию API, включая пути из всех субмодулей, с использованием корректных путей (и префиксов) и корректных тегов:
|
||||
|
||||
<img src="/img/tutorial/bigger-applications/image01.png">
|
||||
|
||||
## Подключение существующего маршрута через новый префикс (`prefix`) { #include-the-same-router-multiple-times-with-different-prefix }
|
||||
## Подключение одного и того же маршрутизатора несколько раз с разными `prefix` { #include-the-same-router-multiple-times-with-different-prefix }
|
||||
|
||||
Вы можете использовать `.include_router()` несколько раз с одним и тем же маршрутом, применив различные префиксы.
|
||||
Вы можете использовать `.include_router()` несколько раз с *одним и тем же* маршрутизатором, используя разные префиксы.
|
||||
|
||||
Это может быть полезным, если нужно предоставить доступ к одному и тому же API через различные префиксы, например, `/api/v1` и `/api/latest`.
|
||||
Это может быть полезно, например, чтобы предоставить доступ к одному и тому же API с разными префиксами, например `/api/v1` и `/api/latest`.
|
||||
|
||||
Это продвинутый способ, который вам может и не пригодится. Мы приводим его на случай, если вдруг вам это понадобится.
|
||||
Это продвинутое использование, которое вам может и не понадобиться, но оно есть на случай, если понадобится.
|
||||
|
||||
## Включение одного маршрутизатора (`APIRouter`) в другой { #include-an-apirouter-in-another }
|
||||
## Подключение `APIRouter` в другой `APIRouter` { #include-an-apirouter-in-another }
|
||||
|
||||
Точно так же, как вы включаете `APIRouter` в приложение `FastAPI`, вы можете включить `APIRouter` в другой `APIRouter`:
|
||||
Точно так же, как вы можете подключить `APIRouter` к приложению `FastAPI`, вы можете подключить `APIRouter` к другому `APIRouter`, используя:
|
||||
|
||||
```Python
|
||||
router.include_router(other_router)
|
||||
```
|
||||
|
||||
Удостоверьтесь, что вы сделали это до того, как подключить маршрутизатор (`router`) к вашему `FastAPI` приложению, и *эндпоинты* маршрутизатора `other_router` были также подключены.
|
||||
Убедитесь, что вы сделали это до подключения `router` к приложению `FastAPI`, чтобы *операции пути* из `other_router` также были подключены.
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
## Обновление с заменой при помощи `PUT` { #update-replacing-with-put }
|
||||
|
||||
Для полного обновления элемента можно воспользоваться операцией <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
|
||||
Чтобы обновить элемент, вы можете использовать операцию <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT" class="external-link" target="_blank">HTTP `PUT`</a>.
|
||||
|
||||
Вы можете использовать `jsonable_encoder`, чтобы преобразовать входные данные в данные, которые можно сохранить как JSON (например, в NoSQL-базе данных). Например, преобразование `datetime` в `str`.
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *}
|
||||
|
||||
`PUT` используется для получения данных, которые должны полностью заменить существующие данные.
|
||||
`PUT` используется для получения данных, которые должны заменить существующие данные.
|
||||
|
||||
### Предупреждение о замене { #warning-about-replacing }
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
поскольку оно не включает уже сохраненный атрибут `"tax": 20.2`, входная модель примет значение по умолчанию `"tax": 10.5`.
|
||||
|
||||
И данные будут сохранены с этим "новым" `tax`, равным `10,5`.
|
||||
И данные будут сохранены с этим «новым» `tax`, равным `10.5`.
|
||||
|
||||
## Частичное обновление с помощью `PATCH` { #partial-updates-with-patch }
|
||||
|
||||
Также можно использовать <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> операцию для *частичного* обновления данных.
|
||||
Также можно использовать операцию <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH" class="external-link" target="_blank">HTTP `PATCH`</a> для *частичного* обновления данных.
|
||||
|
||||
Это означает, что можно передавать только те данные, которые необходимо обновить, оставляя остальные нетронутыми.
|
||||
|
||||
@@ -46,19 +46,13 @@
|
||||
|
||||
### Использование параметра `exclude_unset` в Pydantic { #using-pydantics-exclude-unset-parameter }
|
||||
|
||||
Если необходимо выполнить частичное обновление, то очень полезно использовать параметр `exclude_unset` в методе `.model_dump()` модели Pydantic.
|
||||
Если вы хотите получать частичные обновления, очень полезно использовать параметр `exclude_unset` в `.model_dump()` модели Pydantic.
|
||||
|
||||
Например, `item.model_dump(exclude_unset=True)`.
|
||||
|
||||
/// info | Информация
|
||||
В результате будет сгенерирован `dict`, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию.
|
||||
|
||||
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_dump()`.
|
||||
|
||||
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_dump()`.
|
||||
|
||||
///
|
||||
|
||||
В результате будет сгенерирован словарь, содержащий только те данные, которые были заданы при создании модели `item`, без учета значений по умолчанию. Затем вы можете использовать это для создания словаря только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию:
|
||||
Затем вы можете использовать это для создания `dict` только с теми данными, которые были установлены (отправлены в запросе), опуская значения по умолчанию:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *}
|
||||
|
||||
@@ -66,14 +60,6 @@
|
||||
|
||||
Теперь можно создать копию существующей модели, используя `.model_copy()`, и передать параметр `update` с `dict`, содержащим данные для обновления.
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic v1 метод назывался `.copy()`, в Pydantic v2 он помечен как устаревший (но все еще поддерживается) и переименован в `.model_copy()`.
|
||||
|
||||
Примеры здесь используют `.copy()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, лучше используйте `.model_copy()`.
|
||||
|
||||
///
|
||||
|
||||
Например, `stored_item_model.model_copy(update=update_data)`:
|
||||
|
||||
{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *}
|
||||
@@ -84,9 +70,9 @@
|
||||
|
||||
* (Опционально) использовать `PATCH` вместо `PUT`.
|
||||
* Извлечь сохранённые данные.
|
||||
* Поместить эти данные в Pydantic модель.
|
||||
* Поместить эти данные в Pydantic-модель.
|
||||
* Сгенерировать `dict` без значений по умолчанию из входной модели (с использованием `exclude_unset`).
|
||||
* Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять значения, уже сохраненные в модели по умолчанию.
|
||||
* Таким образом, можно обновлять только те значения, которые действительно установлены пользователем, вместо того чтобы переопределять значения, уже сохраненные, значениями по умолчанию из вашей модели.
|
||||
* Создать копию хранимой модели, обновив ее атрибуты полученными частичными обновлениями (с помощью параметра `update`).
|
||||
* Преобразовать скопированную модель в то, что может быть сохранено в вашей БД (например, с помощью `jsonable_encoder`).
|
||||
* Это сравнимо с повторным использованием метода модели `.model_dump()`, но при этом происходит проверка (и преобразование) значений в типы данных, которые могут быть преобразованы в JSON, например, `datetime` в `str`.
|
||||
@@ -97,7 +83,7 @@
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Эту же технику можно использовать и для операции HTTP `PUT`.
|
||||
На самом деле эту же технику можно использовать и для операции HTTP `PUT`.
|
||||
|
||||
Но в приведенном примере используется `PATCH`, поскольку он был создан именно для таких случаев использования.
|
||||
|
||||
|
||||
@@ -32,9 +32,10 @@
|
||||
|
||||
{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *}
|
||||
|
||||
|
||||
Так же, как при объявлении параметров запроса: когда атрибут модели имеет значение по умолчанию, он не обязателен. Иначе он обязателен. Используйте `None`, чтобы сделать его просто необязательным.
|
||||
|
||||
Например, модель выше описывает такой JSON "объект" (или Python `dict`):
|
||||
Например, модель выше описывает такой JSON "`object`" (или Python `dict`):
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -45,7 +46,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "объект" тоже будет корректным:
|
||||
...так как `description` и `tax` являются необязательными (со значением по умолчанию `None`), такой JSON "`object`" тоже будет корректным:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -73,7 +74,7 @@
|
||||
* Передаст полученные данные в параметр `item`.
|
||||
* Поскольку внутри функции вы объявили его с типом `Item`, у вас будет поддержка со стороны редактора кода (автозавершение и т. п.) для всех атрибутов и их типов.
|
||||
* Сгенерирует определения <a href="https://json-schema.org" class="external-link" target="_blank">JSON Schema</a> для вашей модели; вы можете использовать их и в других местах, если это имеет смысл для вашего проекта.
|
||||
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией <abbr title="User Interfaces – Пользовательские интерфейсы">UIs</abbr>.
|
||||
* Эти схемы будут частью сгенерированной схемы OpenAPI и будут использоваться автоматической документацией <abbr title="User Interfaces - Пользовательские интерфейсы">UIs</abbr>.
|
||||
|
||||
## Автоматическая документация { #automatic-docs }
|
||||
|
||||
@@ -127,14 +128,6 @@ JSON Schema ваших моделей будет частью сгенериро
|
||||
|
||||
{* ../../docs_src/body/tutorial002_py310.py *}
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
|
||||
|
||||
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы можете использовать Pydantic v2, используйте `.model_dump()`.
|
||||
|
||||
///
|
||||
|
||||
## Тело запроса + параметры пути { #request-body-path-parameters }
|
||||
|
||||
Вы можете одновременно объявить параметры пути и тело запроса.
|
||||
@@ -143,6 +136,7 @@ JSON Schema ваших моделей будет частью сгенериро
|
||||
|
||||
{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *}
|
||||
|
||||
|
||||
## Тело запроса + параметры пути + параметры запроса { #request-body-path-query-parameters }
|
||||
|
||||
Вы также можете одновременно объявить параметры **тела**, **пути** и **запроса**.
|
||||
@@ -153,7 +147,7 @@ JSON Schema ваших моделей будет частью сгенериро
|
||||
|
||||
Параметры функции будут распознаны следующим образом:
|
||||
|
||||
* Если параметр также объявлен в **пути**, он будет использоваться как параметр пути.
|
||||
* Если параметр также объявлен в **пути**, он будет использоваться как path-параметр.
|
||||
* Если параметр имеет **скалярный тип** (например, `int`, `float`, `str`, `bool` и т. п.), он будет интерпретирован как параметр **запроса**.
|
||||
* Если параметр объявлен как тип **модели Pydantic**, он будет интерпретирован как **тело** запроса.
|
||||
|
||||
@@ -161,7 +155,7 @@ JSON Schema ваших моделей будет частью сгенериро
|
||||
|
||||
FastAPI понимает, что значение `q` не является обязательным из-за значения по умолчанию `= None`.
|
||||
|
||||
Аннотации типов `str | None` (Python 3.10+) или `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
|
||||
Аннотации типов `str | None` (Python 3.10+) или `Union` в `Union[str, None]` (Python 3.9+) не используются FastAPI для определения обязательности; он узнает, что параметр не обязателен, потому что у него есть значение по умолчанию `= None`.
|
||||
|
||||
Но добавление аннотаций типов позволит вашему редактору кода лучше вас поддерживать и обнаруживать ошибки.
|
||||
|
||||
@@ -169,4 +163,4 @@ FastAPI понимает, что значение `q` не является об
|
||||
|
||||
## Без Pydantic { #without-pydantic }
|
||||
|
||||
Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Тело — Несколько параметров: Единичные значения в теле](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
Если вы не хотите использовать модели Pydantic, вы также можете использовать параметры **Body**. См. раздел документации [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
||||
@@ -22,21 +22,13 @@
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *}
|
||||
|
||||
/// info | Информация
|
||||
### Про `**user_in.model_dump()` { #about-user-in-model-dump }
|
||||
|
||||
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
|
||||
#### `.model_dump()` из Pydantic { #pydantics-model-dump }
|
||||
|
||||
В примерах здесь используется `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, следует использовать `.model_dump()`.
|
||||
`user_in` — это Pydantic-модель класса `UserIn`.
|
||||
|
||||
///
|
||||
|
||||
### Про `**user_in.dict()` { #about-user-in-dict }
|
||||
|
||||
#### `.dict()` из Pydantic { #pydantics-dict }
|
||||
|
||||
`user_in` - это Pydantic-модель класса `UserIn`.
|
||||
|
||||
У Pydantic-моделей есть метод `.dict()`, который возвращает `dict` с данными модели.
|
||||
У Pydantic-моделей есть метод `.model_dump()`, который возвращает `dict` с данными модели.
|
||||
|
||||
Поэтому, если мы создадим Pydantic-объект `user_in` таким способом:
|
||||
|
||||
@@ -47,10 +39,10 @@ user_in = UserIn(username="john", password="secret", email="john.doe@example.com
|
||||
и затем вызовем:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
user_dict = user_in.model_dump()
|
||||
```
|
||||
|
||||
то теперь у нас есть `dict` с данными модели в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели).
|
||||
то теперь у нас есть `dict` с данными в переменной `user_dict` (это `dict` вместо объекта Pydantic-модели).
|
||||
|
||||
И если мы вызовем:
|
||||
|
||||
@@ -58,7 +50,7 @@ user_dict = user_in.dict()
|
||||
print(user_dict)
|
||||
```
|
||||
|
||||
мы можем получить `dict` с такими данными:
|
||||
мы получим Python `dict` с:
|
||||
|
||||
```Python
|
||||
{
|
||||
@@ -71,7 +63,7 @@ print(user_dict)
|
||||
|
||||
#### Распаковка `dict` { #unpacking-a-dict }
|
||||
|
||||
Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python распакует его. Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение.
|
||||
Если мы возьмём `dict` наподобие `user_dict` и передадим его в функцию (или класс), используя `**user_dict`, Python его "распакует". Он передаст ключи и значения `user_dict` напрямую как аргументы типа ключ-значение.
|
||||
|
||||
Поэтому, продолжая описанный выше пример с `user_dict`, написание такого кода:
|
||||
|
||||
@@ -79,7 +71,7 @@ print(user_dict)
|
||||
UserInDB(**user_dict)
|
||||
```
|
||||
|
||||
Будет работать так же, как примерно такой код:
|
||||
будет эквивалентно:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
@@ -90,7 +82,7 @@ UserInDB(
|
||||
)
|
||||
```
|
||||
|
||||
Или, если для большей точности мы напрямую используем `user_dict` с любым потенциальным содержимым, то этот пример будет выглядеть так:
|
||||
Или, более точно, если использовать `user_dict` напрямую, с любым содержимым, которое он может иметь в будущем:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
@@ -101,22 +93,22 @@ UserInDB(
|
||||
)
|
||||
```
|
||||
|
||||
#### Pydantic-модель из содержимого другой модели { #a-pydantic-model-from-the-contents-of-another }
|
||||
#### Pydantic-модель из содержимого другой { #a-pydantic-model-from-the-contents-of-another }
|
||||
|
||||
Как в примере выше мы получили `user_dict` из `user_in.dict()`, этот код:
|
||||
Как в примере выше мы получили `user_dict` из `user_in.model_dump()`, этот код:
|
||||
|
||||
```Python
|
||||
user_dict = user_in.dict()
|
||||
user_dict = user_in.model_dump()
|
||||
UserInDB(**user_dict)
|
||||
```
|
||||
|
||||
будет равнозначен такому:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict())
|
||||
UserInDB(**user_in.model_dump())
|
||||
```
|
||||
|
||||
...потому что `user_in.dict()` - это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` и ставим перед ним `**`.
|
||||
...потому что `user_in.model_dump()` — это `dict`, и затем мы указываем, чтобы Python его "распаковал", когда передаём его в `UserInDB` с префиксом `**`.
|
||||
|
||||
Таким образом мы получаем Pydantic-модель на основе данных из другой Pydantic-модели.
|
||||
|
||||
@@ -125,10 +117,10 @@ UserInDB(**user_in.dict())
|
||||
И затем, если мы добавим дополнительный именованный аргумент `hashed_password=hashed_password` как здесь:
|
||||
|
||||
```Python
|
||||
UserInDB(**user_in.dict(), hashed_password=hashed_password)
|
||||
UserInDB(**user_in.model_dump(), hashed_password=hashed_password)
|
||||
```
|
||||
|
||||
... то мы получим что-то подобное:
|
||||
...то в итоге получится что-то подобное:
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
@@ -142,13 +134,13 @@ UserInDB(
|
||||
|
||||
/// warning | Предупреждение
|
||||
|
||||
Вспомогательные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность.
|
||||
Вспомогательные дополнительные функции `fake_password_hasher` и `fake_save_user` используются только для демонстрации возможного потока данных и, конечно, не обеспечивают настоящую безопасность.
|
||||
|
||||
///
|
||||
|
||||
## Сократите дублирование { #reduce-duplication }
|
||||
|
||||
Сокращение дублирования кода - это одна из главных идей **FastAPI**.
|
||||
Сокращение дублирования кода — это одна из главных идей **FastAPI**.
|
||||
|
||||
Поскольку дублирование кода повышает риск появления багов, проблем с безопасностью, проблем десинхронизации кода (когда вы обновляете код в одном месте, но не обновляете в другом), и т.д.
|
||||
|
||||
@@ -160,13 +152,13 @@ UserInDB(
|
||||
|
||||
Все операции конвертации, валидации, документации, и т.п. будут по-прежнему работать нормально.
|
||||
|
||||
В этом случае мы можем определить только различия между моделями (с `password` в чистом виде, с `hashed_password` и без пароля):
|
||||
В этом случае мы можем определить только различия между моделями (с паролем в чистом виде `password`, с `hashed_password` и без пароля):
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *}
|
||||
|
||||
## `Union` или `anyOf` { #union-or-anyof }
|
||||
|
||||
Вы можете определить ответ как `Union` из двух или более типов. Это означает, что ответ должен соответствовать одному из них.
|
||||
Вы можете объявить HTTP-ответ как `Union` из двух или более типов. Это означает, что HTTP-ответ может быть любым из них.
|
||||
|
||||
Он будет определён в OpenAPI как `anyOf`.
|
||||
|
||||
@@ -174,7 +166,7 @@ UserInDB(
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
При объявлении <a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a>, сначала указывайте наиболее детальные типы, затем менее детальные. В примере ниже более детальный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`.
|
||||
При объявлении <a href="https://docs.pydantic.dev/latest/concepts/types/#unions" class="external-link" target="_blank">`Union`</a> сначала указывайте наиболее специфичный тип, затем менее специфичный. В примере ниже более специфичный `PlaneItem` стоит перед `CarItem` в `Union[PlaneItem, CarItem]`.
|
||||
|
||||
///
|
||||
|
||||
@@ -192,19 +184,19 @@ UserInDB(
|
||||
some_variable: PlaneItem | CarItem
|
||||
```
|
||||
|
||||
Но если мы помещаем его в `response_model=PlaneItem | CarItem` мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа.
|
||||
Но если мы поместим это в присваивание `response_model=PlaneItem | CarItem`, мы получим ошибку, потому что Python попытается произвести **некорректную операцию** между `PlaneItem` и `CarItem` вместо того, чтобы интерпретировать это как аннотацию типа.
|
||||
|
||||
## Список моделей { #list-of-models }
|
||||
|
||||
Таким же образом вы можете определять ответы как списки объектов.
|
||||
Таким же образом вы можете объявлять HTTP-ответы, возвращающие списки объектов.
|
||||
|
||||
Для этого используйте `typing.List` из стандартной библиотеки Python (или просто `list` в Python 3.9 и выше):
|
||||
Для этого используйте стандартный `typing.List` в Python (или просто `list` в Python 3.9 и выше):
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
|
||||
## Ответ с произвольным `dict` { #response-with-arbitrary-dict }
|
||||
|
||||
Вы также можете определить ответ, используя произвольный одноуровневый `dict` и определяя только типы ключей и значений без использования Pydantic-моделей.
|
||||
Вы также можете объявить HTTP-ответ, используя обычный произвольный `dict`, объявив только тип ключей и значений, без использования Pydantic-модели.
|
||||
|
||||
Это полезно, если вы заранее не знаете корректных названий полей/атрибутов (которые будут нужны при использовании Pydantic-модели).
|
||||
|
||||
@@ -214,6 +206,6 @@ some_variable: PlaneItem | CarItem
|
||||
|
||||
## Резюме { #recap }
|
||||
|
||||
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждой из них.
|
||||
Используйте несколько Pydantic-моделей и свободно применяйте наследование для каждого случая.
|
||||
|
||||
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояния с полями `password`, `password_hash` и без пароля.
|
||||
Вам не обязательно иметь единственную модель данных для каждой сущности, если эта сущность должна иметь возможность быть в разных "состояниях". Как в случае с "сущностью" пользователя, у которого есть состояние, включающее `password`, `password_hash` и отсутствие пароля.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Query-параметр `q` имеет тип `str | None`, это означает, что он имеет тип `str`, но также может быть `None`. Значение по умолчанию действительно `None`, поэтому FastAPI будет знать, что он не обязателен.
|
||||
|
||||
/// note | Технические детали
|
||||
/// note | Примечание
|
||||
|
||||
FastAPI поймёт, что значение `q` не обязательно, из‑за значения по умолчанию `= None`.
|
||||
|
||||
@@ -31,7 +31,7 @@ FastAPI поймёт, что значение `q` не обязательно,
|
||||
|
||||
/// info | Дополнительная информация
|
||||
|
||||
Поддержка `Annotated` (и рекомендация использовать его) появилась в FastAPI версии 0.95.0.
|
||||
Поддержка `Annotated` (и рекомендация использовать его) появилась в FastAPI версии <abbr title="before 2023-03">0.95.0</abbr>.
|
||||
|
||||
Если у вас более старая версия, при попытке использовать `Annotated` вы получите ошибки.
|
||||
|
||||
@@ -109,7 +109,7 @@ q: Annotated[Union[str, None]] = None
|
||||
|
||||
## Альтернатива (устаревшее): `Query` как значение по умолчанию { #alternative-old-query-as-the-default-value }
|
||||
|
||||
В предыдущих версиях FastAPI (до <abbr title="до 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
|
||||
В предыдущих версиях FastAPI (до <abbr title="before 2023-03">0.95.0</abbr>) требовалось использовать `Query` как значение по умолчанию для параметра вместо помещения его в `Annotated`. Скорее всего вы ещё встретите такой код, поэтому поясню.
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
@@ -177,7 +177,7 @@ q: str = Query(default="rick")
|
||||
|
||||
**Значение по умолчанию** у **параметра функции** — это **настоящее значение по умолчанию**, что более интуитивно для Python. 😌
|
||||
|
||||
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор кода** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
|
||||
Вы можете **вызвать** эту же функцию в **других местах** без FastAPI, и она будет **работать как ожидается**. Если есть **обязательный** параметр (без значения по умолчанию), ваш **редактор** сообщит об ошибке, **Python** тоже пожалуется, если вы запустите её без передачи обязательного параметра.
|
||||
|
||||
Если вы не используете `Annotated`, а применяете **(устаревший) стиль со значением по умолчанию**, то при вызове этой функции без FastAPI в **других местах** вам нужно **помнить** о том, что надо передать аргументы, чтобы всё работало корректно, иначе значения будут не такими, как вы ожидаете (например, вместо `str` будет `QueryInfo` или что-то подобное). И ни редактор, ни Python не будут ругаться при самом вызове функции — ошибка проявится лишь при операциях внутри.
|
||||
|
||||
@@ -191,7 +191,7 @@ q: str = Query(default="rick")
|
||||
|
||||
## Регулярные выражения { #add-regular-expressions }
|
||||
|
||||
Вы можете определить <abbr title="Регулярное выражение (regex, regexp) — это последовательность символов, задающая шаблон поиска для строк.">регулярное выражение</abbr> `pattern`, которому должен соответствовать параметр:
|
||||
Вы можете определить <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">регулярное выражение</abbr> `pattern`, которому должен соответствовать параметр:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
@@ -205,20 +205,6 @@ q: str = Query(default="rick")
|
||||
|
||||
Теперь вы знаете, что когда они понадобятся, вы сможете использовать их в **FastAPI**.
|
||||
|
||||
### `regex` из Pydantic v1 вместо `pattern` { #pydantic-v1-regex-instead-of-pattern }
|
||||
|
||||
До Pydantic версии 2 и до FastAPI 0.100.0 этот параметр назывался `regex`, а не `pattern`, но сейчас он устарел.
|
||||
|
||||
Вы всё ещё можете встретить такой код:
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_regex_an_py310.py hl[11] *}
|
||||
|
||||
////
|
||||
|
||||
Имейте в виду, что это устарело, и код следует обновить на использование нового параметра `pattern`. 🤓
|
||||
|
||||
## Значения по умолчанию { #default-values }
|
||||
|
||||
Конечно, можно использовать и другие значения по умолчанию, не только `None`.
|
||||
@@ -279,7 +265,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
вы получите множественные значения query-параметра `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции обработки пути*, в *параметре функции* `q`.
|
||||
вы получите множественные значения *query-параметров* `q` (`foo` и `bar`) в виде Python-`list` внутри вашей *функции-обработчика пути*, в *параметре функции* `q`.
|
||||
|
||||
Таким образом, ответ на этот URL будет:
|
||||
|
||||
@@ -331,7 +317,7 @@ http://localhost:8000/items/
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
|
||||
/// note | Технические детали
|
||||
/// note | Примечание
|
||||
|
||||
Имейте в виду, что в этом случае FastAPI не будет проверять содержимое списка.
|
||||
|
||||
@@ -343,9 +329,9 @@ http://localhost:8000/items/
|
||||
|
||||
Можно добавить больше информации о параметре.
|
||||
|
||||
Эта информация будет включена в сгенерированную OpenAPI-схему и использована интерфейсами документации и внешними инструментами.
|
||||
Эта информация будет включена в сгенерированную OpenAPI и использована интерфейсами документации и внешними инструментами.
|
||||
|
||||
/// note | Технические детали
|
||||
/// note | Примечание
|
||||
|
||||
Помните, что разные инструменты могут иметь разный уровень поддержки OpenAPI.
|
||||
|
||||
@@ -385,7 +371,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
Предположим, этот параметр вам больше не нравится.
|
||||
|
||||
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <abbr title="устаревший, не рекомендуется использовать">устаревший</abbr>.
|
||||
Его нужно оставить на какое‑то время, так как клиенты его используют, но вы хотите, чтобы в документации он явно отображался как <abbr title="obsolete, recommended not to use it">устаревший</abbr>.
|
||||
|
||||
Тогда передайте параметр `deprecated=True` в `Query`:
|
||||
|
||||
@@ -415,7 +401,7 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
///
|
||||
|
||||
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="ISBN означает International Standard Book Number – Международный стандартный книжный номер">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="IMDB (Internet Movie Database) — веб‑сайт с информацией о фильмах">IMDB</abbr>:
|
||||
Например, эта кастомная проверка убеждается, что ID элемента начинается с `isbn-` для номера книги <abbr title="ISBN means International Standard Book Number">ISBN</abbr> или с `imdb-` для ID URL фильма на <abbr title="IMDB (Internet Movie Database) is a website with information about movies">IMDB</abbr>:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
@@ -449,13 +435,13 @@ http://127.0.0.1:8000/items/?item-query=foobaritems
|
||||
|
||||
#### Случайный элемент { #a-random-item }
|
||||
|
||||
С помощью `data.items()` мы получаем <abbr title="Объект, по которому можно итерироваться циклом for, например список, множество и т. п.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
|
||||
С помощью `data.items()` мы получаем <abbr title="Something we can iterate on with a for loop, like a list, set, etc.">итерируемый объект</abbr> с кортежами, содержащими ключ и значение для каждого элемента словаря.
|
||||
|
||||
Мы превращаем этот итерируемый объект в обычный `list` через `list(data.items())`.
|
||||
|
||||
Затем с `random.choice()` можно получить **случайное значение** из списка — то есть кортеж вида `(id, name)`. Это будет что‑то вроде `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`.
|
||||
|
||||
После этого мы **распаковываем** эти два значения кортежа в переменные `id` и `name`.
|
||||
После этого мы **присваиваем эти два значения** кортежа переменным `id` и `name`.
|
||||
|
||||
Так что, если пользователь не передал ID элемента, он всё равно получит случайную рекомендацию.
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
|
||||
|
||||
FastAPI будет использовать этот тип ответа для:
|
||||
FastAPI будет использовать этот возвращаемый тип, чтобы:
|
||||
|
||||
* **Валидации** возвращаемых данных.
|
||||
* Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен. В таком случае будет возвращена ошибка сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру.
|
||||
* Добавления **JSON Schema** для ответа в OpenAPI *операции пути*.
|
||||
* **Валидировать** возвращаемые данные.
|
||||
* Если данные невалидны (например, отсутствует поле), это означает, что код *вашего* приложения работает некорректно и возвращает не то, что должен, и он вернёт ошибку сервера вместо неправильных данных. Так вы и ваши клиенты можете быть уверены, что получите ожидаемые данные и ожидаемую структуру данных.
|
||||
* Добавить **JSON Schema** для ответа в OpenAPI *операции пути*.
|
||||
* Это будет использовано **автоматической документацией**.
|
||||
* Это также будет использовано инструментами автоматической генерации клиентского кода.
|
||||
|
||||
@@ -23,7 +23,7 @@ FastAPI будет использовать этот тип ответа для:
|
||||
|
||||
Бывают случаи, когда вам нужно или хочется возвращать данные, которые не в точности соответствуют объявленному типу.
|
||||
|
||||
Например, вы можете хотеть **возвращать словарь (dict)** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных).
|
||||
Например, вы можете хотеть **возвращать словарь** или объект из базы данных, но **объявить его как Pydantic-модель**. Тогда Pydantic-модель выполнит всё документирование данных, валидацию и т.п. для объекта, который вы вернули (например, словаря или объекта из базы данных).
|
||||
|
||||
Если вы добавите аннотацию возвращаемого типа, инструменты и редакторы кода начнут жаловаться (и будут правы), что функция возвращает тип (например, dict), отличный от объявленного (например, Pydantic-модель).
|
||||
|
||||
@@ -41,19 +41,19 @@ FastAPI будет использовать этот тип ответа для:
|
||||
|
||||
/// note | Примечание
|
||||
|
||||
Обратите внимание, что `response_model` — это параметр метода «декоратора» (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в которой указываются параметры и тело запроса.
|
||||
Обратите внимание, что `response_model` — это параметр метода «декоратора» (`get`, `post` и т.д.), а не вашей *функции-обработчика пути*, в которой указываются все параметры и тело запроса.
|
||||
|
||||
///
|
||||
|
||||
`response_model` принимает тот же тип, что вы бы объявили для поля Pydantic-модели, то есть это может быть одна Pydantic-модель, а может быть, например, `list` Pydantic-моделей, как `List[Item]`.
|
||||
|
||||
FastAPI будет использовать `response_model` для документации, валидации и т. п., а также для **конвертации и фильтрации выходных данных** к объявленному типу.
|
||||
FastAPI будет использовать этот `response_model` для документирования данных, валидации и т.п., а также для **конвертации и фильтрации выходных данных** к объявленному типу.
|
||||
|
||||
/// tip | Совет
|
||||
|
||||
Если у вас в редакторе кода, mypy и т. п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`.
|
||||
Если у вас в редакторе кода, mypy и т.п. включены строгие проверки типов, вы можете объявить возвращаемый тип функции как `Any`.
|
||||
|
||||
Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документацию данных, валидацию, фильтрацию и т.д. с помощью `response_model`.
|
||||
Так вы сообщите редактору, что намеренно возвращаете что угодно. Но FastAPI всё равно выполнит документирование данных, валидацию, фильтрацию и т.д. с помощью `response_model`.
|
||||
|
||||
///
|
||||
|
||||
@@ -61,7 +61,7 @@ FastAPI будет использовать `response_model` для докуме
|
||||
|
||||
Если вы объявите и возвращаемый тип, и `response_model`, приоритет будет у `response_model`, именно его использует FastAPI.
|
||||
|
||||
Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`.
|
||||
Так вы можете добавить корректные аннотации типов к своим функциям, даже если фактически возвращаете тип, отличный от модели ответа, чтобы ими пользовались редактор кода и инструменты вроде mypy. И при этом FastAPI продолжит выполнять валидацию данных, документацию и т.д. с использованием `response_model`.
|
||||
|
||||
Вы также можете указать `response_model=None`, чтобы отключить создание модели ответа для данной *операции пути*. Это может понадобиться, если вы добавляете аннотации типов для вещей, не являющихся валидными полями Pydantic. Пример вы увидите ниже.
|
||||
|
||||
@@ -75,7 +75,7 @@ FastAPI будет использовать `response_model` для докуме
|
||||
|
||||
Чтобы использовать `EmailStr`, сначала установите <a href="https://github.com/JoshData/python-email-validator" class="external-link" target="_blank">`email-validator`</a>.
|
||||
|
||||
Создайте [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активируйте его и затем установите пакет, например:
|
||||
Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установите пакет, например:
|
||||
|
||||
```console
|
||||
$ pip install email-validator
|
||||
@@ -105,7 +105,7 @@ $ pip install "pydantic[email]"
|
||||
|
||||
///
|
||||
|
||||
## Добавить модель для ответа { #add-an-output-model }
|
||||
## Добавить выходную модель { #add-an-output-model }
|
||||
|
||||
Вместо этого мы можем создать входную модель с паролем в открытом виде и выходную модель без него:
|
||||
|
||||
@@ -123,7 +123,7 @@ $ pip install "pydantic[email]"
|
||||
|
||||
### `response_model` или возвращаемый тип { #response-model-or-return-type }
|
||||
|
||||
В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы.
|
||||
В этом случае, поскольку две модели различаются, если бы мы аннотировали возвращаемый тип функции как `UserOut`, редактор кода и инструменты пожаловались бы, что мы возвращаем неверный тип, так как это разные классы.
|
||||
|
||||
Поэтому в этом примере мы должны объявить тип ответа в параметре `response_model`.
|
||||
|
||||
@@ -131,37 +131,37 @@ $ pip install "pydantic[email]"
|
||||
|
||||
## Возвращаемый тип и фильтрация данных { #return-type-and-data-filtering }
|
||||
|
||||
Продолжим предыдущий пример. Мы хотели **аннотировать функцию одним типом**, но при этом иметь возможность вернуть из функции что-то, что фактически включает **больше данных**.
|
||||
Продолжим предыдущий пример. Мы хотели **аннотировать функцию одним типом**, но при этом хотели иметь возможность вернуть из функции что-то, что фактически включает **больше данных**.
|
||||
|
||||
Мы хотим, чтобы FastAPI продолжал **фильтровать** данные с помощью модели ответа. Так что, даже если функция возвращает больше данных, в ответ будут включены только поля, объявленные в модели ответа.
|
||||
|
||||
В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора и инструментов, проверяющих возвращаемый тип функции.
|
||||
В предыдущем примере, поскольку классы были разными, нам пришлось использовать параметр `response_model`. Но это также означает, что мы теряем поддержку от редактора кода и инструментов, проверяющих возвращаемый тип функции.
|
||||
|
||||
Однако в большинстве таких случаев нам нужно лишь **отфильтровать/убрать** некоторые данные, как в этом примере.
|
||||
|
||||
И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе и инструментах и при этом получить **фильтрацию данных** от FastAPI.
|
||||
И в этих случаях мы можем использовать классы и наследование, чтобы воспользоваться **аннотациями типов** функций для лучшей поддержки в редакторе кода и инструментах и при этом получить **фильтрацию данных** от FastAPI.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *}
|
||||
|
||||
Так мы получаем поддержку инструментов (редакторы, mypy) — код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI.
|
||||
Так мы получаем поддержку инструментов — редакторов кода и mypy, так как этот код корректен с точки зрения типов — и одновременно получаем фильтрацию данных от FastAPI.
|
||||
|
||||
Как это работает? Давайте разберёмся. 🤓
|
||||
|
||||
### Аннотации типов и инструменты { #type-annotations-and-tooling }
|
||||
|
||||
Сначала посмотрим, как это увидят редакторы, mypy и другие инструменты.
|
||||
Сначала посмотрим, как это увидят редактор кода, mypy и другие инструменты.
|
||||
|
||||
`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он включает все поля обеих моделей.
|
||||
`BaseUser` содержит базовые поля. Затем `UserIn` наследуется от `BaseUser` и добавляет поле `password`, то есть он будет включать все поля обеих моделей.
|
||||
|
||||
Мы аннотируем возвращаемый тип функции как `BaseUser`, но фактически возвращаем экземпляр `UserIn`.
|
||||
|
||||
Редактор, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`.
|
||||
Редактор кода, mypy и другие инструменты не будут возражать, потому что с точки зрения типов `UserIn` — подкласс `BaseUser`, что означает, что это *валидный* тип везде, где ожидается что-то, являющееся `BaseUser`.
|
||||
|
||||
### Фильтрация данных FastAPI { #fastapi-data-filtering }
|
||||
|
||||
Теперь, для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе.
|
||||
Теперь для FastAPI: он увидит возвращаемый тип и убедится, что то, что вы возвращаете, включает **только** поля, объявленные в этом типе.
|
||||
|
||||
FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы вернуть гораздо больше данных, чем ожидали.
|
||||
FastAPI делает несколько вещей внутри вместе с Pydantic, чтобы гарантировать, что те же правила наследования классов не используются для фильтрации возвращаемых данных, иначе вы могли бы в итоге вернуть намного больше данных, чем ожидали.
|
||||
|
||||
Таким образом вы получаете лучшее из обоих миров: аннотации типов с **поддержкой инструментов** и **фильтрацию данных**.
|
||||
|
||||
@@ -171,17 +171,17 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
<img src="/img/tutorial/response-model/image01.png">
|
||||
|
||||
И обе модели используются в интерактивной документации API:
|
||||
И обе модели будут использоваться в интерактивной документации API:
|
||||
|
||||
<img src="/img/tutorial/response-model/image02.png">
|
||||
|
||||
## Другие аннотации возвращаемых типов { #other-return-type-annotations }
|
||||
|
||||
Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор, mypy и т. д.).
|
||||
Бывают случаи, когда вы возвращаете что-то, что не является валидным полем Pydantic, и аннотируете это в функции только ради поддержки инструментов (редактор кода, mypy и т.д.).
|
||||
|
||||
### Возврат Response напрямую { #return-a-response-directly }
|
||||
|
||||
Самый распространённый случай — [возвращать Response напрямую, как описано далее в разделах для продвинутых](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
Самый распространённый случай — [возвращать Response напрямую, как описано далее в продвинутой документации](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
|
||||
@@ -195,7 +195,7 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
|
||||
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот случай.
|
||||
Это тоже сработает, так как `RedirectResponse` — подкласс `Response`, и FastAPI автоматически обработает этот простой случай.
|
||||
|
||||
### Некорректные аннотации возвращаемых типов { #invalid-return-type-annotations }
|
||||
|
||||
@@ -209,15 +209,15 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
### Отключить модель ответа { #disable-response-model }
|
||||
|
||||
Продолжая пример выше, вы можете не хотеть использовать стандартную валидацию данных, документацию, фильтрацию и т.д., выполняемые FastAPI.
|
||||
Продолжая пример выше, вы можете не хотеть использовать стандартные валидацию данных, документирование, фильтрацию и т.п., выполняемые FastAPI.
|
||||
|
||||
Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов (редакторы, проверки типов вроде mypy).
|
||||
Но при этом вы можете хотеть сохранить аннотацию возвращаемого типа в функции, чтобы пользоваться поддержкой инструментов вроде редакторов кода и проверяющих типов инструментов (например, mypy).
|
||||
|
||||
В этом случае вы можете отключить генерацию модели ответа, установив `response_model=None`:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *}
|
||||
|
||||
Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, не влияя на ваше приложение FastAPI. 🤓
|
||||
Так FastAPI пропустит генерацию модели ответа, и вы сможете использовать любые аннотации возвращаемых типов, которые вам нужны, без влияния на ваше приложение FastAPI. 🤓
|
||||
|
||||
## Параметры кодирования модели ответа { #response-model-encoding-parameters }
|
||||
|
||||
@@ -252,20 +252,6 @@ FastAPI делает несколько вещей внутри вместе с
|
||||
|
||||
/// info | Информация
|
||||
|
||||
В Pydantic v1 метод назывался `.dict()`, в Pydantic v2 он был помечен как устаревший (но всё ещё поддерживается) и переименован в `.model_dump()`.
|
||||
|
||||
Примеры здесь используют `.dict()` для совместимости с Pydantic v1, но если вы используете Pydantic v2, применяйте `.model_dump()`.
|
||||
|
||||
///
|
||||
|
||||
/// info | Информация
|
||||
|
||||
FastAPI использует метод `.dict()` у Pydantic-моделей с <a href="https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict" class="external-link" target="_blank">параметром `exclude_unset`</a>, чтобы добиться такого поведения.
|
||||
|
||||
///
|
||||
|
||||
/// info | Информация
|
||||
|
||||
Вы также можете использовать:
|
||||
|
||||
* `response_model_exclude_defaults=True`
|
||||
@@ -312,7 +298,7 @@ FastAPI достаточно умен (на самом деле, это Pydantic
|
||||
|
||||
Обратите внимание, что значения по умолчанию могут быть любыми, не только `None`.
|
||||
|
||||
Это может быть список (`[]`), число с плавающей точкой `10.5` и т. д.
|
||||
Это может быть список (`[]`), число с плавающей точкой `10.5` и т.д.
|
||||
|
||||
///
|
||||
|
||||
@@ -346,7 +332,7 @@ FastAPI достаточно умен (на самом деле, это Pydantic
|
||||
|
||||
#### Использование `list` вместо `set` { #using-lists-instead-of-sets }
|
||||
|
||||
Если вы забыли использовать `set` и применили `list` или `tuple`, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно:
|
||||
Если вы забыли использовать `set` и применили `list` или `tuple` вместо него, FastAPI всё равно преобразует это в `set`, и всё будет работать корректно:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *}
|
||||
|
||||
|
||||
@@ -8,36 +8,14 @@
|
||||
|
||||
Вы можете объявить `examples` для модели Pydantic, которые будут добавлены в сгенерированную JSON Schema.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial001_pv1_py310.py hl[13:23] *}
|
||||
|
||||
////
|
||||
|
||||
Эта дополнительная информация будет добавлена как есть в выходную **JSON Schema** этой модели и будет использоваться в документации API.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
В Pydantic версии 2 вы будете использовать атрибут `model_config`, который принимает `dict`, как описано в <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">Документации Pydantic: Конфигурация</a>.
|
||||
Вы можете использовать атрибут `model_config`, который принимает `dict`, как описано в <a href="https://docs.pydantic.dev/latest/api/config/" class="external-link" target="_blank">Документации Pydantic: Конфигурация</a>.
|
||||
|
||||
Вы можете задать `"json_schema_extra"` с `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
В Pydantic версии 1 вы будете использовать внутренний класс `Config` и `schema_extra`, как описано в <a href="https://docs.pydantic.dev/1.10/usage/schema/#schema-customization" class="external-link" target="_blank">Документации Pydantic: Настройка схемы</a>.
|
||||
|
||||
Вы можете задать `schema_extra` со `dict`, содержащим любые дополнительные данные, которые вы хотите видеть в сгенерированной JSON Schema, включая `examples`.
|
||||
|
||||
////
|
||||
|
||||
/// tip | Подсказка
|
||||
|
||||
Вы можете использовать тот же приём, чтобы расширить JSON Schema и добавить свою собственную дополнительную информацию.
|
||||
@@ -102,13 +80,13 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб
|
||||
|
||||
Ещё до того как **JSON Schema** поддержала `examples`, в OpenAPI была поддержка другого поля, также называемого `examples`.
|
||||
|
||||
Эти **специфические для OpenAPI** `examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях для каждой операции пути (обработчика пути)**, а не внутри каждого объекта Schema.
|
||||
Эти **специфические для OpenAPI** `examples` находятся в другой секции спецификации OpenAPI. Они находятся в **подробностях для каждой операции пути (обработчика пути)**, а не внутри каждого объекта JSON Schema.
|
||||
|
||||
И Swagger UI уже какое‑то время поддерживает именно это поле `examples`. Поэтому вы можете использовать его, чтобы **отобразить** разные **примеры в UI документации**.
|
||||
|
||||
Структура этого специфичного для OpenAPI поля `examples` — это `dict` с **несколькими примерами** (вместо `list`), каждый с дополнительной информацией, которая также будет добавлена в **OpenAPI**.
|
||||
|
||||
Это не помещается внутрь каждого объекта Schema в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
|
||||
Это не помещается внутрь каждого объекта JSON Schema в OpenAPI, это находится снаружи, непосредственно на уровне самой *операции пути*.
|
||||
|
||||
### Использование параметра `openapi_examples` { #using-the-openapi-examples-parameter }
|
||||
|
||||
@@ -124,7 +102,7 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб
|
||||
|
||||
Ключи `dict` идентифицируют каждый пример, а каждое значение — это ещё один `dict`.
|
||||
|
||||
Каждый конкретный пример‑`dict` в `examples` может содержать:
|
||||
Каждый конкретный пример `dict` в `examples` может содержать:
|
||||
|
||||
* `summary`: Краткое описание примера.
|
||||
* `description`: Подробное описание, которое может содержать текст в Markdown.
|
||||
@@ -135,7 +113,7 @@ OpenAPI 3.1.0 (используется начиная с FastAPI 0.99.0) доб
|
||||
|
||||
{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *}
|
||||
|
||||
### OpenAPI-примеры в UI документации { #openapi-examples-in-the-docs-ui }
|
||||
### Примеры OpenAPI в UI документации { #openapi-examples-in-the-docs-ui }
|
||||
|
||||
С `openapi_examples`, добавленным в `Body()`, страница `/docs` будет выглядеть так:
|
||||
|
||||
@@ -213,7 +191,7 @@ OpenAPI также добавила поля `example` и `examples` в друг
|
||||
|
||||
### Swagger UI и специфичные для OpenAPI `examples` { #swagger-ui-and-openapi-specific-examples }
|
||||
|
||||
Раньше, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации.
|
||||
Теперь, поскольку Swagger UI не поддерживал несколько примеров JSON Schema (по состоянию на 2023-08-26), у пользователей не было способа показать несколько примеров в документации.
|
||||
|
||||
Чтобы решить это, FastAPI `0.103.0` **добавил поддержку** объявления того же старого, **специфичного для OpenAPI**, поля `examples` с новым параметром `openapi_examples`. 🤓
|
||||
|
||||
|
||||
Reference in New Issue
Block a user