Модели форм¶
🌐 Перевод выполнен с помощью ИИ и людей
Этот перевод был сделан ИИ под руководством людей. 🤝
В нем могут быть ошибки из-за неправильного понимания оригинального смысла или неестественности и т. д. 🤖
Вы можете улучшить этот перевод, помогая нам лучше направлять ИИ LLM.
Вы можете использовать Pydantic-модели для объявления полей формы в FastAPI.
Заметка
Чтобы использовать формы, сначала установите 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 извлечёт данные для каждого поля из данных формы в HTTP-запросе и выдаст вам объявленную Pydantic-модель.
Для HTML-флажков объявляйте булевы поля со значением по умолчанию False.
Проверьте документацию¶
Вы можете проверить это в интерфейсе документации по адресу /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
Если клиент попробует отправить дополнительные данные, то он получит HTTP-ответ с ошибкой.
Например, если клиент попытается отправить поля формы:
username:Rickpassword:Portal Gunextra:Mr. Poopybutthole
Он получит HTTP-ответ с ошибкой, сообщающий ему, что поле 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.
Итоги¶
Вы можете использовать Pydantic-модели для объявления полей форм в FastAPI. 😎