12 KiB
Body - Вложенные модели
С помощью FastAPI, вы можете определять, валидировать, документировать и использовать модели произвольной вложенности (благодаря библиотеке Pydantic).
Определение полей содержащих списки
Вы можете определять атрибут как подтип. Например, тип list
в Python:
{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *}
Это приведёт к тому, что обьект tags
преобразуется в список, несмотря на то что тип его элементов не объявлен.
Определение полей содержащих список с определением типов его элементов
Однако в Python есть способ объявления списков с указанием типов для вложенных элементов:
Импортируйте List
из модуля typing
В Python 3.9 и выше вы можете использовать стандартный тип list
для объявления аннотаций типов, как мы увидим ниже. 💡
Но в версиях Python до 3.9 (начиная с 3.6) сначала вам необходимо импортировать List
из стандартного модуля typing
в Python:
{* ../../docs_src/body_nested_models/tutorial002.py hl[1] *}
Объявление list
с указанием типов для вложенных элементов
Объявление типов для элементов (внутренних типов) вложенных в такие типы как list
, dict
, tuple
:
- Если у вас Python версии ниже чем 3.9, импортируйте их аналог из модуля
typing
- Передайте внутренний(ие) тип(ы) как "параметры типа", используя квадратные скобки:
[
и]
В Python версии 3.9 это будет выглядеть так:
my_list: list[str]
В версиях Python до 3.9 это будет выглядеть так:
from typing import List
my_list: List[str]
Это всё стандартный синтаксис Python для объявления типов.
Используйте этот же стандартный синтаксис для атрибутов модели с внутренними типами.
Таким образом, в нашем примере мы можем явно указать тип данных для поля tags
как "список строк":
{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *}
Типы множеств
Но затем мы подумали и поняли, что теги не должны повторяться и, вероятно, они должны быть уникальными строками.
И в Python есть специальный тип данных для множеств уникальных элементов - set
.
Тогда мы можем обьявить поле tags
как множество строк:
{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *}
С помощью этого, даже если вы получите запрос с повторяющимися данными, они будут преобразованы в множество уникальных элементов.
И когда вы выводите эти данные, даже если исходный набор содержал дубликаты, они будут выведены в виде множества уникальных элементов.
И они также будут соответствующим образом аннотированы / задокументированы.
Вложенные Модели
У каждого атрибута Pydantic-модели есть тип.
Но этот тип может сам быть другой моделью Pydantic.
Таким образом вы можете объявлять глубоко вложенные JSON "объекты" с определёнными именами атрибутов, типами и валидацией.
Всё это может быть произвольно вложенным.
Определение подмодели
Например, мы можем определить модель Image
:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *}
Использование вложенной модели в качестве типа
Также мы можем использовать эту модель как тип атрибута:
{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *}
Это означает, что FastAPI будет ожидать тело запроса, аналогичное этому:
{
"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
.
Чтобы увидеть все варианты, которые у вас есть, ознакомьтесь с документацией по необычным типам Pydantic. Вы увидите некоторые примеры в следующей главе.
Например, так как в модели Image
у нас есть поле url
, то мы можем объявить его как тип HttpUrl
из модуля Pydantic вместо типа str
:
{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *}
Строка будет проверена на соответствие допустимому URL-адресу и задокументирована в JSON схему / OpenAPI.
Атрибуты, содержащие списки подмоделей
Вы также можете использовать модели Pydantic в качестве типов вложенных в list
, set
и т.д:
{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *}
Такая реализация будет ожидать (конвертировать, валидировать, документировать и т.д) JSON-содержимое в следующем формате:
{
"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
есть список объектов изображений.
///
Глубоко вложенные модели
Вы можете определять модели с произвольным уровнем вложенности:
{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *}
/// info | Информация
Заметьте, что у объекта Offer
есть список объектов Item
, которые, в свою очередь, могут содержать необязательный список объектов Image
///
Тела с чистыми списками элементов
Если верхний уровень значения тела JSON-объекта представляет собой JSON array
(в Python - list
), вы можете объявить тип в параметре функции, так же, как в моделях Pydantic:
images: List[Image]
в Python 3.9 и выше:
images: list[Image]
например так:
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
Универсальная поддержка редактора
И вы получаете поддержку редактора везде.
Даже для элементов внутри списков:

Вы не могли бы получить такую поддержку редактора, если бы работали напрямую с dict
, а не с моделями Pydantic.
Но вы также не должны беспокоиться об этом, входящие словари автоматически конвертируются, а ваш вывод также автоматически преобразуется в формат JSON.
Тела запросов с произвольными словарями (dict
)
Вы также можете объявить тело запроса как dict
с ключами определенного типа и значениями другого типа данных.
Без необходимости знать заранее, какие значения являются допустимыми для имён полей/атрибутов (как это было бы в случае с моделями Pydantic).
Это было бы полезно, если вы хотите получить ключи, которые вы еще не знаете.
Другой полезный случай - когда вы хотите чтобы ключи были другого типа данных, например, int
.
Именно это мы сейчас и увидим здесь.
В этом случае вы принимаете dict
, пока у него есть ключи типа int
со значениями типа float
:
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
/// tip | Совет
Имейте в виду, что JSON поддерживает только ключи типа str
.
Но Pydantic обеспечивает автоматическое преобразование данных.
Это значит, что даже если пользователи вашего API могут отправлять только строки в качестве ключей, при условии, что эти строки содержат целые числа, Pydantic автоматический преобразует и валидирует эти данные.
А dict
, с именем weights
, который вы получите в качестве ответа Pydantic, действительно будет иметь ключи типа int
и значения типа float
.
///
Резюме
С помощью FastAPI вы получаете максимальную гибкость, предоставляемую моделями Pydantic, сохраняя при этом простоту, краткость и элегантность вашего кода.
И дополнительно вы получаете:
- Поддержку редактора (автодополнение доступно везде!)
- Преобразование данных (также известно как парсинг / сериализация)
- Валидацию данных
- Документацию схемы данных
- Автоматическую генерацию документации