# Body - Вложенные модели { #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`.

Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с <a href="https://docs.pydantic.dev/latest/concepts/types/" class="external-link" target="_blank">обзором типов Pydantic</a>. Вы увидите некоторые примеры в следующей главе.

Например, так как в модели `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"
        }
    ]
}
```

/// info | Информация

Заметьте, что теперь у ключа `images` есть список объектов изображений.

///

## Глубоко вложенные модели { #deeply-nested-models }

Вы можете определять модели с произвольным уровнем вложенности:

{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}

/// info | Информация

Заметьте, что у объекта `Offer` есть список объектов `Item`, которые, в свою очередь, могут содержать необязательный список объектов `Image`

///

## Тела с чистыми списками элементов { #bodies-of-pure-lists }

Если верхний уровень значения тела JSON-объекта представляет собой JSON `array` (в Python — `list`), вы можете объявить тип в параметре функции, так же как в моделях Pydantic:

```Python
images: list[Image]
```

например так:

{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}

## Поддержка редактора кода везде { #editor-support-everywhere }

И вы получаете поддержку редактора кода везде.

Даже для элементов внутри списков:

<img src="/img/tutorial/body-nested-models/image01.png">

Вы не могли бы получить такую поддержку редактора кода, если бы работали напрямую с `dict`, а не с моделями Pydantic.

Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON.

## Тела запросов с произвольными словарями (`dict`) { #bodies-of-arbitrary-dicts }

Вы также можете объявить тело запроса как `dict` с ключами определённого типа и значениями другого типа.

Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic).

Это было бы полезно, если вы хотите получить ключи, которые вы ещё не знаете.

---

Другой полезный случай — когда вы хотите, чтобы ключи были другого типа данных, например, `int`.

Именно это мы сейчас и увидим здесь.

В этом случае вы принимаете любой `dict`, пока у него есть ключи типа `int` со значениями типа `float`:

{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}

/// tip | Совет

Имейте в виду, что JSON поддерживает только ключи типа `str`.

Но Pydantic обеспечивает автоматическое преобразование данных.

Это значит, что даже если клиенты вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматически преобразует и валидирует эти данные.

А `dict`, который вы получите как `weights`, действительно будет иметь ключи типа `int` и значения типа `float`.

///

## Резюме { #recap }

С помощью **FastAPI** вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода.

И дополнительно вы получаете:

* Поддержку редактора кода (автозавершение доступно везде!)
* Преобразование данных (также известно как парсинг / сериализация)
* Валидацию данных
* Документацию схемы данных
* Автоматическую генерацию документации
