# Тіло запиту - Вкладені моделі З **FastAPI** Ви можете визначати, перевіряти, документувати та використовувати моделі, які можуть бути вкладені на будь-яку глибину (завдяки Pydantic). ## Поля списку Ви можете визначити атрибут як підтип. Наприклад, Python-список (`list`): {* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *} Це зробить `tags` списком, хоча не визначається тип елементів списку. ## Поля списку з параметром типу Але Python має специфічний спосіб оголошення списків з внутрішніми типами або "параметрами типу": ### Імпортуємо `List` з модуля typing У Python 3.9 і вище можна використовувати стандартний `list` для оголошення таких типів, як ми побачимо нижче. 💡 Але в Python версії до 3.9 (від 3.6 і вище) спочатку потрібно імпортувати `List` з модуля стандартної бібліотеки Python `typing`: {* ../../docs_src/body_nested_models/tutorial002.py hl[1] *} ### Оголошення `list` з параметром типу Щоб оголосити типи з параметрами типу (внутрішніми типами), такими як `list`, `dict`, `tuple`: * Якщо Ви використовуєте версію Python до 3.9, імпортуйте їх відповідну версію з модуля `typing`. * Передайте внутрішні типи як "параметри типу", використовуючи квадратні дужки: `[` and `]`. У Python 3.9 це буде виглядати так: ```Python my_list: list[str] ``` У версіях Python до 3.9 це виглядає так: ```Python 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** очікуватиме тіло запиту такого вигляду: ```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** Ви отримуєте: * Підтримку в редакторі (автозавершення тощо), навіть для вкладених моделей * Конвертацію даних * Валідацію даних * Автоматичну документацію ## Спеціальні типи та валідація Окрім звичайних типів, таких як `str`, `int`, `float`, та ін. Ви можете використовувати складніші типи, які наслідують `str`. Щоб побачити всі доступні варіанти, ознайомтеся з оглядом типів у Pydantic. Деякі приклади будуть у наступних розділах. Наприклад, у моделі `Image` є поле `url`, тому ми можемо оголосити його як `HttpUrl` від Pydantic замість `str`: {* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *} Рядок буде перевірено як дійсну URL-адресу і задокументовано в JSON Schema / OpenAPI як URL. ## Атрибути зі списками підмоделей У Pydantic Ви можете використовувати моделі як підтипи для `list`, `set` тощо: {* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *} Це означає, що **FastAPI** буде очікувати (конвертувати, валідувати, документувати тощо) 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` містить список об'єктів зображень. /// ## Глибоко вкладені моделі Ви можете визначати вкладені моделі довільної глибини: {* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *} /// info | Інформація Зверніть увагу, що в моделі `Offer` є список `Item`ів, які, своєю чергою, можуть мати необов'язковий список `Image`ів. /// ## Тіла запитів, що складаються зі списків Якщо верхній рівень JSON тіла, яке Ви очікуєте, є JSON `масивом` (у Python — `list`), Ви можете оголосити тип у параметрі функції, як і в моделях Pydantic: ```Python images: List[Image] ``` або в Python 3.9 і вище: ```Python images: list[Image] ``` наприклад: {* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *} ## Підтримка в редакторі всюди Ви отримаєте підтримку в редакторі всюди. Навіть для елементів у списках: Ви не змогли б отримати таку підтримку в редакторі, якби працювали напряму зі `dict`, а не з моделями Pydantic. Але Вам не потрібно турбуватися про це: вхідні dict'и автоматично конвертуються, а вихідні дані автоматично перетворюються в 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`, матиме ключі типу `int` та значення типу `float`. /// ## Підсумок З **FastAPI** Ви маєте максимальну гнучкість завдяки моделям Pydantic, зберігаючи при цьому код простим, коротким та елегантним. А також отримуєте всі переваги: * Підтримка в редакторі (автодоповнення всюди!) * Конвертація даних (парсинг/сериалізація) * Валідація даних * Документація схем * Автоматичне створення документації