Browse Source
Co-authored-by: ivan-abc <[email protected]> Co-authored-by: Vladislav Kramorenko <[email protected]>pull/9720/head
committed by
GitHub
2 changed files with 383 additions and 0 deletions
@ -0,0 +1,382 @@ |
|||
# Body - Вложенные модели |
|||
|
|||
С помощью **FastAPI**, вы можете определять, валидировать, документировать и использовать модели произвольной вложенности (благодаря библиотеке Pydantic). |
|||
|
|||
## Определение полей содержащих списки |
|||
|
|||
Вы можете определять атрибут как подтип. Например, тип `list` в Python: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial001_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial001.py!} |
|||
``` |
|||
|
|||
Это приведёт к тому, что обьект `tags` преобразуется в список, несмотря на то что тип его элементов не объявлен. |
|||
|
|||
## Определение полей содержащих список с определением типов его элементов |
|||
|
|||
Однако в Python есть способ объявления списков с указанием типов для вложенных элементов: |
|||
|
|||
### Импортируйте `List` из модуля typing |
|||
|
|||
В Python 3.9 и выше вы можете использовать стандартный тип `list` для объявления аннотаций типов, как мы увидим ниже. 💡 |
|||
|
|||
Но в версиях Python до 3.9 (начиная с 3.6) сначала вам необходимо импортировать `List` из стандартного модуля `typing` в Python: |
|||
|
|||
```Python hl_lines="1" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
### Объявление `list` с указанием типов для вложенных элементов |
|||
|
|||
Объявление типов для элементов (внутренних типов) вложенных в такие типы как `list`, `dict`, `tuple`: |
|||
|
|||
* Если у вас Python версии ниже чем 3.9, импортируйте их аналог из модуля `typing` |
|||
* Передайте внутренний(ие) тип(ы) как "параметры типа", используя квадратные скобки: `[` и `]` |
|||
|
|||
В Python версии 3.9 это будет выглядеть так: |
|||
|
|||
```Python |
|||
my_list: list[str] |
|||
``` |
|||
|
|||
В версиях Python до 3.9 это будет выглядеть так: |
|||
|
|||
```Python |
|||
from typing import List |
|||
|
|||
my_list: List[str] |
|||
``` |
|||
|
|||
Это всё стандартный синтаксис Python для объявления типов. |
|||
|
|||
Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами. |
|||
|
|||
Таким образом, в нашем примере мы можем явно указать тип данных для поля `tags` как "список строк": |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial002.py!} |
|||
``` |
|||
|
|||
## Типы множеств |
|||
|
|||
Но затем мы подумали и поняли, что теги не должны повторяться и, вероятно, они должны быть уникальными строками. |
|||
|
|||
И в Python есть специальный тип данных для множеств уникальных элементов - `set`. |
|||
|
|||
Тогда мы может обьявить поле `tags` как множество строк: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="12" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="1 14" |
|||
{!> ../../../docs_src/body_nested_models/tutorial003.py!} |
|||
``` |
|||
|
|||
С помощью этого, даже если вы получите запрос с повторяющимися данными, они будут преобразованы в множество уникальных элементов. |
|||
|
|||
И когда вы выводите эти данные, даже если исходный набор содержал дубликаты, они будут выведены в виде множества уникальных элементов. |
|||
|
|||
И они также будут соответствующим образом аннотированы / задокументированы. |
|||
|
|||
## Вложенные Модели |
|||
|
|||
У каждого атрибута Pydantic-модели есть тип. |
|||
|
|||
Но этот тип может сам быть другой моделью Pydantic. |
|||
|
|||
Таким образом вы можете объявлять глубоко вложенные JSON "объекты" с определёнными именами атрибутов, типами и валидацией. |
|||
|
|||
Всё это может быть произвольно вложенным. |
|||
|
|||
### Определение подмодели |
|||
|
|||
Например, мы можем определить модель `Image`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7-9" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9-11" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="9-11" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
### Использование вложенной модели в качестве типа |
|||
|
|||
Также мы можем использовать эту модель как тип атрибута: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial004.py!} |
|||
``` |
|||
|
|||
Это означает, что **FastAPI** будет ожидать тело запроса, аналогичное этому: |
|||
|
|||
```JSON |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2, |
|||
"tags": ["rock", "metal", "bar"], |
|||
"image": { |
|||
"url": "http://example.com/baz.jpg", |
|||
"name": "The Foo live" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Ещё раз: сделав такое объявление, с помощью **FastAPI** вы получите: |
|||
|
|||
* Поддержку редакторов IDE (автодополнение и т.д), даже для вложенных моделей |
|||
* Преобразование данных |
|||
* Валидацию данных |
|||
* Автоматическую документацию |
|||
|
|||
## Особые типы и валидация |
|||
|
|||
Помимо обычных простых типов, таких как `str`, `int`, `float`, и т.д. Вы можете использовать более сложные базовые типы, которые наследуются от типа `str`. |
|||
|
|||
Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с документацией <a href="https://pydantic-docs.helpmanual.io/usage/types/" class="external-link" target="_blank">по необычным типам Pydantic</a>. Вы увидите некоторые примеры в следующей главе. |
|||
|
|||
Например, так как в модели `Image` у нас есть поле `url`, то мы можем объявить его как тип `HttpUrl` из модуля Pydantic вместо типа `str`: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="2 8" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="4 10" |
|||
{!> ../../../docs_src/body_nested_models/tutorial005.py!} |
|||
``` |
|||
|
|||
Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON схему / OpenAPI. |
|||
|
|||
## Атрибуты, содержащие списки подмоделей |
|||
|
|||
Вы также можете использовать модели Pydantic в качестве типов вложенных в `list`, `set` и т.д: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="18" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="20" |
|||
{!> ../../../docs_src/body_nested_models/tutorial006.py!} |
|||
``` |
|||
|
|||
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д) JSON-содержимое в следующем формате: |
|||
|
|||
```JSON hl_lines="11" |
|||
{ |
|||
"name": "Foo", |
|||
"description": "The pretender", |
|||
"price": 42.0, |
|||
"tax": 3.2, |
|||
"tags": [ |
|||
"rock", |
|||
"metal", |
|||
"bar" |
|||
], |
|||
"images": [ |
|||
{ |
|||
"url": "http://example.com/baz.jpg", |
|||
"name": "The Foo live" |
|||
}, |
|||
{ |
|||
"url": "http://example.com/dave.jpg", |
|||
"name": "The Baz" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
!!! info "Информация" |
|||
Заметьте, что теперь у ключа `images` есть список объектов изображений. |
|||
|
|||
## Глубоко вложенные модели |
|||
|
|||
Вы можете определять модели с произвольным уровнем вложенности: |
|||
|
|||
=== "Python 3.10+" |
|||
|
|||
```Python hl_lines="7 12 18 21 25" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007_py310.py!} |
|||
``` |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="9 14 20 23 27" |
|||
{!> ../../../docs_src/body_nested_models/tutorial007.py!} |
|||
``` |
|||
|
|||
!!! info "Информация" |
|||
Заметьте, что у объекта `Offer` есть список объектов `Item`, которые, в свою очередь, могут содержать необязательный список объектов `Image` |
|||
|
|||
## Тела с чистыми списками элементов |
|||
|
|||
Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python - `list`), вы можете объявить тип в параметре функции, так же, как в моделях Pydantic: |
|||
|
|||
```Python |
|||
images: List[Image] |
|||
``` |
|||
|
|||
в Python 3.9 и выше: |
|||
|
|||
```Python |
|||
images: list[Image] |
|||
``` |
|||
|
|||
например так: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="13" |
|||
{!> ../../../docs_src/body_nested_models/tutorial008_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="15" |
|||
{!> ../../../docs_src/body_nested_models/tutorial008.py!} |
|||
``` |
|||
|
|||
## Универсальная поддержка редактора |
|||
|
|||
И вы получаете поддержку редактора везде. |
|||
|
|||
Даже для элементов внутри списков: |
|||
|
|||
<img src="/img/tutorial/body-nested-models/image01.png"> |
|||
|
|||
Вы не могли бы получить такую поддержку редактора, если бы работали напрямую с `dict`, а не с моделями Pydantic. |
|||
|
|||
Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON. |
|||
|
|||
## Тела запросов с произвольными словарями (`dict` ) |
|||
|
|||
Вы также можете объявить тело запроса как `dict` с ключами определенного типа и значениями другого типа данных. |
|||
|
|||
Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic). |
|||
|
|||
Это было бы полезно, если вы хотите получить ключи, которые вы еще не знаете. |
|||
|
|||
--- |
|||
|
|||
Другой полезный случай - когда вы хотите чтобы ключи были другого типа данных, например, `int`. |
|||
|
|||
Именно это мы сейчас и увидим здесь. |
|||
|
|||
В этом случае вы принимаете `dict`, пока у него есть ключи типа `int` со значениями типа `float`: |
|||
|
|||
=== "Python 3.9+" |
|||
|
|||
```Python hl_lines="7" |
|||
{!> ../../../docs_src/body_nested_models/tutorial009_py39.py!} |
|||
``` |
|||
|
|||
=== "Python 3.6+" |
|||
|
|||
```Python hl_lines="9" |
|||
{!> ../../../docs_src/body_nested_models/tutorial009.py!} |
|||
``` |
|||
|
|||
!!! tip "Совет" |
|||
Имейте в виду, что JSON поддерживает только ключи типа `str`. |
|||
|
|||
Но Pydantic обеспечивает автоматическое преобразование данных. |
|||
|
|||
Это значит, что даже если пользователи вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматический преобразует и валидирует эти данные. |
|||
|
|||
А `dict`, с именем `weights`, который вы получите в качестве ответа Pydantic, действительно будет иметь ключи типа `int` и значения типа `float`. |
|||
|
|||
## Резюме |
|||
|
|||
С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода. |
|||
|
|||
И дополнительно вы получаете: |
|||
|
|||
* Поддержку редактора (автодополнение доступно везде!) |
|||
* Преобразование данных (также известно как парсинг / сериализация) |
|||
* Валидацию данных |
|||
* Документацию схемы данных |
|||
* Автоматическую генерацию документации |
Loading…
Reference in new issue