Thanks to visit codestin.com
Credit goes to fastapi.tiangolo.com

Перейти до змісту

Тіло - Вкладені моделі

🌐 Переклад ШІ та людьми

Цей переклад виконано ШІ під керівництвом людей. 🤝

Можливі помилки через неправильне розуміння початкового змісту або неприродні формулювання тощо. 🤖

Ви можете покращити цей переклад, допомігши нам краще спрямовувати AI LLM.

Англійська версія

З FastAPI ви можете визначати, перевіряти, документувати та використовувати моделі, які можуть бути вкладені на будь-яку глибину (завдяки Pydantic).

Поля списку

Ви можете визначити атрибут як підтип. Наприклад, Python-список (list):

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list = []


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Це зробить tags списком, хоча не визначається тип елементів списку.

Поля списку з параметром типу

Але Python має специфічний спосіб оголошення списків з внутрішніми типами або «параметрами типу»:

Оголошення list з параметром типу

Щоб оголосити типи з параметрами типу (внутрішніми типами), такими як list, dict, tuple, передайте внутрішні тип(и) як «параметри типу», використовуючи квадратні дужки: [ та ]

my_list: list[str]

Це стандартний синтаксис Python для оголошення типів.

Використовуйте той самий стандартний синтаксис для атрибутів моделей з внутрішніми типами.

Отже, у нашому прикладі, ми можемо зробити tags саме «списком строк»:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Типи множин

Але потім ми подумали, що теги не повинні повторюватися, вони, ймовірно, повинні бути унікальними строками.

І Python має спеціальний тип даних для множин унікальних елементів - це set.

Тому ми можемо оголосити tags як множину строк:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Навіть якщо ви отримаєте запит з дубльованими даними, він буде перетворений у множину унікальних елементів.

І коли ви будете виводити ці дані, навіть якщо джерело містить дублікати, вони будуть виведені як множина унікальних елементів.

І це буде анотовано / документовано відповідно.

Вкладені моделі

Кожен атрибут моделі Pydantic має тип.

Але цей тип сам може бути іншою моделлю Pydantic.

Отже, ви можете оголосити глибоко вкладені JSON «об'єкти» з конкретними іменами атрибутів, типами та перевірками.

Усе це, вкладене без обмежень.

Визначення підмоделі

Наприклад, ми можемо визначити модель Image:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Використання підмоделі як типу

А потім ми можемо використовувати її як тип атрибута:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Це означатиме, що 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 ви отримуєте:

  • Підтримку в редакторі (автозавершення тощо), навіть для вкладених моделей
  • Конвертацію даних
  • Валідацію даних
  • Автоматичну документацію

Спеціальні типи та валідація

Окрім звичайних одиничних типів, таких як str, int, float, та ін. ви можете використовувати складніші одиничні типи, які наслідують str.

Щоб побачити всі доступні варіанти, ознайомтеся з Оглядом типів у Pydantic. Деякі приклади будуть у наступному розділі.

Наприклад, оскільки в моделі Image є поле url, ми можемо оголосити його як екземпляр HttpUrl від Pydantic замість str:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Строку буде перевірено як дійсну URL-адресу і задокументовано в Схемі JSON / OpenAPI як таку.

Атрибути зі списками підмоделей

У Pydantic ви також можете використовувати моделі як підтипи для list, set тощо:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

Це очікуватиме (конвертуватиме, валідуватиме, документуватиме тощо) тіло 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"
        }
    ]
}

Примітка

Зверніть увагу, що тепер ключ images містить список об'єктів зображень.

Глибоко вкладені моделі

Ви можете визначати вкладені моделі довільної глибини:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None


class Offer(BaseModel):
    name: str
    description: str | None = None
    price: float
    items: list[Item]


@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer

Примітка

Зверніть увагу, що Offer має список Itemів, які, своєю чергою, мають необов'язковий список Imageів

Тіла запитів, що складаються зі списків

Якщо значення верхнього рівня JSON тіла, яке ви очікуєте, є JSON array (Python list), ви можете оголосити тип у параметрі функції так само, як у моделях Pydantic:

images: list[Image]

як у:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

app = FastAPI()


class Image(BaseModel):
    url: HttpUrl
    name: str


@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
    return images

Підтримка в редакторі всюди

І ви отримаєте підтримку в редакторі всюди.

Навіть для елементів у списках:

Ви не змогли б отримати таку підтримку в редакторі, якби працювали напряму зі dict, а не з моделями Pydantic.

Але вам також не потрібно турбуватися про них: вхідні словники автоматично конвертуються, а вихідні дані автоматично перетворюються в JSON.

Тіла з довільними dict

Ви також можете оголосити тіло як dict з ключами одного типу та значеннями іншого типу.

Таким чином, вам не потрібно наперед знати, які імена полів/атрибутів є дійсними (як це було б у випадку з моделями Pydantic).

Це буде корисно, якщо ви хочете приймати ключі, які заздалегідь невідомі.


Інший корисний випадок - коли ви хочете мати ключі іншого типу (наприклад, int).

Ось що ми розглянемо тут.

У цьому випадку ви можете приймати будь-який dict, якщо він має ключі int зі значеннями float:

from fastapi import FastAPI

app = FastAPI()


@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
    return weights

Порада

Майте на увазі, що JSON підтримує лише str як ключі.

Але Pydantic має автоматичну конвертацію даних.

Це означає, що навіть якщо клієнти вашого API можуть надсилати лише строки як ключі, якщо ці строки містять цілі числа, Pydantic конвертує їх і проведе валідацію.

І dict, який ви отримаєте як weights, фактично матиме ключі типу int та значення типу float.

Підсумок

З FastAPI ви маєте максимальну гнучкість завдяки моделям Pydantic, зберігаючи при цьому код простим, коротким та елегантним.

Але з усіма перевагами:

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