# Тело запроса - Вложенные модели { #body-nested-models } С помощью **FastAPI** вы можете определять, валидировать, документировать и использовать модели произвольной глубины вложенности (благодаря Pydantic). ## Поля-списки { #list-fields } Вы можете определить атрибут как подтип. Например, Python-тип `list`: {* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *} Это приведёт к тому, что `tags` будет списком, несмотря на то, что тип его элементов не объявлен. ## Поля-списки с параметром типа { #list-fields-with-type-parameter } Но в Python есть специальный способ объявлять списки с внутренними типами, или «параметрами типа»: ### Объявите `list` с параметром типа { #declare-a-list-with-a-type-parameter } Для объявления типов, у которых есть параметры типа (внутренние типы), таких как `list`, `dict`, `tuple`, передайте внутренний(ие) тип(ы) как «параметры типа», используя квадратные скобки: `[` и `]` ```Python my_list: list[str] ``` Это всё стандартный синтаксис Python для объявления типов. Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами. Таким образом, в нашем примере мы можем явно указать тип данных для поля `tags` как «список строк»: {* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *} ## Типы множеств { #set-types } Но затем мы подумали и поняли, что теги не должны повторяться, вероятно, это должны быть уникальные строки. И в Python есть специальный тип данных для множеств уникальных элементов — `set`. Тогда мы можем объявить поле `tags` как множество строк: {* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *} С помощью этого, даже если вы получите запрос с повторяющимися данными, они будут преобразованы в множество уникальных элементов. И когда вы выводите эти данные, даже если исходный набор содержал дубликаты, они будут выведены в виде множества уникальных элементов. И они также будут соответствующим образом аннотированы / задокументированы. ## Вложенные модели { #nested-models } У каждого атрибута Pydantic-модели есть тип. Но этот тип сам может быть другой моделью Pydantic. Таким образом, вы можете объявлять глубоко вложенные JSON «объекты» с определёнными именами атрибутов, типами и валидацией. Всё это может быть произвольно вложенным. ### Определение подмодели { #define-a-submodel } Например, мы можем определить модель `Image`: {* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *} ### Использование подмодели как типа { #use-the-submodel-as-a-type } Также мы можем использовать эту модель как тип атрибута: {* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *} Это означает, что **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** вы получите: * Поддержку редактора кода (автозавершение и т.д.), даже для вложенных моделей * Преобразование данных * Валидацию данных * Автоматическую документацию ## Особые типы и валидация { #special-types-and-validation } Помимо обычных простых типов, таких как `str`, `int`, `float` и т.д., вы можете использовать более сложные простые типы, которые наследуются от `str`. Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с [обзором типов Pydantic](https://pydantic.dev/docs/validation/latest/concepts/types/). Вы увидите некоторые примеры в следующей главе. Например, так как в модели `Image` у нас есть поле `url`, то мы можем объявить его как тип `HttpUrl` из Pydantic вместо типа `str`: {* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *} Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON Schema / OpenAPI как таковая. ## Атрибуты, содержащие списки подмоделей { #attributes-with-lists-of-submodels } Вы также можете использовать модели Pydantic в качестве подтипов для `list`, `set` и т.д.: {* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *} Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д.) 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" } ] } ``` /// note | Примечание Заметьте, что теперь у ключа `images` есть список объектов изображений. /// ## Глубоко вложенные модели { #deeply-nested-models } Вы можете определять модели с произвольным уровнем вложенности: {* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *} /// note | Примечание Заметьте, что у объекта `Offer` есть список объектов `Item`, которые, в свою очередь, могут содержать необязательный список объектов `Image` /// ## Тела запросов с чистыми списками элементов { #bodies-of-pure-lists } Если верхний уровень значения JSON-тела запроса представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic: ```Python images: list[Image] ``` например так: {* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *} ## Поддержка редактора кода везде { #editor-support-everywhere } И вы получаете поддержку редактора кода везде. Даже для элементов внутри списков: Вы не могли бы получить такую поддержку редактора кода, если бы работали напрямую с `dict`, а не с моделями Pydantic. Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON. ## Тела запросов с произвольными словарями (`dict`) { #bodies-of-arbitrary-dicts } Вы также можете объявить тело запроса как `dict` с ключами определённого типа и значениями другого типа. Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic). Это было бы полезно, если вы хотите получить ключи, которые вы ещё не знаете. --- Другой полезный случай — когда вы хотите, чтобы ключи были другого типа данных, например, `int`. Именно это мы сейчас и увидим здесь. В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`: {* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *} /// tip | Совет Имейте в виду, что JSON поддерживает только ключи типа `str`. Но Pydantic обеспечивает автоматическое преобразование данных. Это значит, что даже если клиенты вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматически преобразует и валидирует эти данные. А `dict`, который вы получите как `weights`, действительно будет иметь ключи типа `int` и значения типа `float`. /// ## Резюме { #recap } С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода. Но со всеми преимуществами: * Поддержку редактора кода (автозавершение доступно везде!) * Преобразование данных (также известно как парсинг / сериализация) * Валидацию данных * Документацию схемы данных * Автоматическую генерацию документации