Browse Source
Co-authored-by: Valentyn Druzhynin <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sofie Van Landeghem <[email protected]>pull/13432/head
committed by
GitHub
1 changed files with 245 additions and 0 deletions
@ -0,0 +1,245 @@ |
|||||
|
# Тіло запиту - Вкладені моделі |
||||
|
|
||||
|
З **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`. |
||||
|
|
||||
|
Щоб побачити всі доступні варіанти, ознайомтеся з оглядом <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 як 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] *} |
||||
|
|
||||
|
## Підтримка в редакторі всюди |
||||
|
|
||||
|
Ви отримаєте підтримку в редакторі всюди. |
||||
|
|
||||
|
Навіть для елементів у списках: |
||||
|
|
||||
|
<img src="/img/tutorial/body-nested-models/image01.png"> |
||||
|
|
||||
|
Ви не змогли б отримати таку підтримку в редакторі, якби працювали напряму зі `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, зберігаючи при цьому код простим, коротким та елегантним. |
||||
|
|
||||
|
А також отримуєте всі переваги: |
||||
|
|
||||
|
* Підтримка в редакторі (автодоповнення всюди!) |
||||
|
* Конвертація даних (парсинг/сериалізація) |
||||
|
* Валідація даних |
||||
|
* Документація схем |
||||
|
* Автоматичне створення документації |
Loading…
Reference in new issue