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

Моделі форм

AI coding agents

Use the official FastAPI skill for your installed version. Install it from your project directory:

uvx library-skills

See Develop with AI.

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

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

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

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

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

У FastAPI ви можете використовувати Pydantic-моделі для оголошення полів форми.

Примітка

Щоб використовувати форми, спочатку встановіть python-multipart.

Додайте його до вашого проєкту:

$ uv add python-multipart

Примітка

Це підтримується, починаючи з FastAPI версії 0.113.0. 🤓

Pydantic-моделі для форм

Вам просто потрібно оголосити Pydantic-модель з полями, які ви хочете отримати як поля форми, а потім оголосити параметр як Form:

from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
    username: str
    password: str


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
    return data
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
    username: str
    password: str


@app.post("/login/")
async def login(data: FormData = Form()):
    return data

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

Для прапорців HTML оголошуйте булеві поля зі значенням за замовчуванням False.

Перевірте документацію

Ви можете перевірити це в UI документації за /docs:

Забороніть додаткові поля форми

У деяких особливих випадках (ймовірно, не дуже поширених) ви можете обмежити поля форми лише тими, які були оголошені в Pydantic-моделі. І заборонити будь-які додаткові поля.

Примітка

Це підтримується, починаючи з FastAPI версії 0.114.0. 🤓

Ви можете використати конфігурацію Pydantic-моделі, щоб заборонити forbid будь-які додаткові extra поля:

from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
    username: str
    password: str
    model_config = {"extra": "forbid"}


@app.post("/login/")
async def login(data: Annotated[FormData, Form()]):
    return data
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class FormData(BaseModel):
    username: str
    password: str
    model_config = {"extra": "forbid"}


@app.post("/login/")
async def login(data: FormData = Form()):
    return data

Якщо клієнт спробує надіслати додаткові дані, він отримає відповідь з помилкою.

Наприклад, якщо клієнт спробує надіслати поля форми:

  • username: Rick
  • password: Portal Gun
  • extra: Mr. Poopybutthole

Він отримає відповідь із помилкою, яка повідомляє, що поле extra не дозволено:

{
    "detail": [
        {
            "type": "extra_forbidden",
            "loc": ["body", "extra"],
            "msg": "Extra inputs are not permitted",
            "input": "Mr. Poopybutthole"
        }
    ]
}

Поєднуйте моделі форм

Ви можете поєднувати кілька моделей форм з окремими параметрами Form.

from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str


class Preferences(BaseModel):
    tags: list[str] = []


@app.post("/profiles/")
async def create_profile(
    profile: Annotated[Profile, Form()],
    preferences: Annotated[Preferences, Form()],
    token: Annotated[str, Form()],
):
    return {"profile": profile, "preferences": preferences, "token": token}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str


class Preferences(BaseModel):
    tags: list[str] = []


@app.post("/profiles/")
async def create_profile(
    profile: Profile = Form(),
    preferences: Preferences = Form(),
    token: str = Form(),
):
    return {"profile": profile, "preferences": preferences, "token": token}

Клієнт надсилає name, tags і token як поля форми. Кожна модель перевіряє поля форми незалежно.

Конфігурація extra кожної моделі застосовується до всіх полів форми, зокрема полів і файлів, оголошених в інших параметрах або залежностях. З extra="forbid" будь-яке поле поза цією моделлю відхиляється. З extra="allow" ці поля включаються як додаткові поля моделі.

Якщо кілька оголошень використовують однакове ім'я поля форми, кожне перевіряє значення цього поля. Використовуйте сумісні типи й обмеження для спільних полів, щоб схема OpenAPI могла описувати їх узгоджено.

Необов'язкові моделі форм

Оголосіть модель форми з | None і значенням за замовчуванням None, щоб зробити групу необов'язковою.

from typing import Annotated

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str
    age: int


@app.post("/profiles/")
async def create_profile(
    token: Annotated[str, Form()],
    profile: Annotated[Profile | None, Form()] = None,
):
    return {"token": token, "profile": profile}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str
    age: int


@app.post("/profiles/")
async def create_profile(
    token: str = Form(),
    profile: Profile | None = Form(default=None),
):
    return {"token": token, "profile": profile}

Якщо не надіслано ані name, ані age, profile має значення None. Якщо надіслано хоча б одне з них, FastAPI перевіряє модель і вимагає обидва поля. Надіслане порожнє значення також вважається наданням поля.

Використання | None без значення за замовчуванням залишає параметр обов'язковим.

Додаткові поля обробляються відповідно до конфігурації extra моделі. Ігноровані додаткові поля не роблять модель присутньою.

Необов'язкова модель, що містить лише невстановлені прапорці, пропускається. Надішліть значення, наприклад enabled=false, щоб надати її явно.

Необов'язкові об'єднання моделей форм

Ви також можете використовувати необов'язкове об'єднання моделей.

from typing import Annotated, Literal

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Person(BaseModel):
    kind: Literal["person"]
    name: str


class Company(BaseModel):
    kind: Literal["company"]
    company_name: str


@app.post("/contacts/")
async def create_contact(
    contact: Annotated[Person | Company | None, Form(discriminator="kind")] = None,
):
    return contact
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from typing import Literal

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Person(BaseModel):
    kind: Literal["person"]
    name: str


class Company(BaseModel):
    kind: Literal["company"]
    company_name: str


@app.post("/contacts/")
async def create_contact(
    contact: Person | Company | None = Form(default=None, discriminator="kind"),
):
    return contact

Якщо жодного поля форми не надіслано, contact має значення None. Надішліть kind=person разом із name або kind=company разом із company_name. Дискримінатор вибирає модель для перевірки.

Поєднуйте об'єднання моделей форм

Ви можете поєднувати об'єднання з іншими моделями форм, окремими параметрами Form і параметрами File. Кожне об'єднання перевіряється незалежно.

from typing import Annotated, Literal

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Person(BaseModel):
    contact_type: Literal["person"]
    name: str


class Company(BaseModel):
    contact_type: Literal["company"]
    company_name: str


class Email(BaseModel):
    notification_type: Literal["email"]
    email: str


class SMS(BaseModel):
    notification_type: Literal["sms"]
    phone: str


@app.post("/contacts/")
async def create_contact(
    contact: Annotated[Person | Company, Form(discriminator="contact_type")],
    token: Annotated[str, Form()],
    notification: Annotated[
        Email | SMS | None, Form(discriminator="notification_type")
    ] = None,
):
    return {"contact": contact, "notification": notification, "token": token}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from typing import Literal

from fastapi import FastAPI, Form
from pydantic import BaseModel

app = FastAPI()


class Person(BaseModel):
    contact_type: Literal["person"]
    name: str


class Company(BaseModel):
    contact_type: Literal["company"]
    company_name: str


class Email(BaseModel):
    notification_type: Literal["email"]
    email: str


class SMS(BaseModel):
    notification_type: Literal["sms"]
    phone: str


@app.post("/contacts/")
async def create_contact(
    contact: Person | Company = Form(discriminator="contact_type"),
    token: str = Form(),
    notification: Email | SMS | None = Form(
        default=None, discriminator="notification_type"
    ),
):
    return {"contact": contact, "notification": notification, "token": token}

Надсилайте всі поля в одній формі. Наприклад, надішліть contact_type=person, name=Rick, notification_type=email, email=rick@example.com і token=secret.

Не надсилайте поля сповіщення, щоб залишити notification зі значенням None.

Моделі форм і файли

Ви також можете поєднувати модель форми з параметрами файлів.

from typing import Annotated

from fastapi import FastAPI, Form, UploadFile
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str


@app.post("/profiles/")
async def create_profile(profile: Annotated[Profile, Form()], avatar: UploadFile):
    return {"profile": profile, "filename": avatar.filename}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, File, Form, UploadFile
from pydantic import BaseModel

app = FastAPI()


class Profile(BaseModel):
    name: str


@app.post("/profiles/")
async def create_profile(profile: Profile = Form(), avatar: UploadFile = File()):
    return {"profile": profile, "filename": avatar.filename}

Тут клієнт надсилає поле name і файл avatar, використовуючи multipart/form-data.

Підсумок

У FastAPI ви можете використовувати Pydantic-моделі для оголошення полів форми. 😎