Ir para o conteúdo

Modelos de Formulários

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.

🌐 Tradução por IA e humanos

Esta tradução foi feita por IA orientada por humanos. 🤝

Ela pode conter erros de interpretação do significado original ou soar pouco natural, etc. 🤖

Você pode melhorar esta tradução ajudando-nos a orientar melhor o LLM de IA.

Versão em inglês

Você pode utilizar Modelos Pydantic para declarar campos de formulários no FastAPI.

Nota

Para utilizar formulários, instale primeiramente o python-multipart.

Adicione-o ao seu projeto:

$ uv add python-multipart

Nota

Isto é suportado desde a versão 0.113.0 do FastAPI. 🤓

Modelos Pydantic para Formulários

Você precisa apenas declarar um modelo Pydantic com os campos que deseja receber como campos de formulários, e então declarar o parâmetro como um 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

O FastAPI irá extrair as informações para cada campo dos dados do formulário na requisição e dar para você o modelo Pydantic que você definiu.

Para caixas de seleção HTML, declare campos booleanos com o valor padrão False.

Confira a Documentação

Você pode verificar na UI de documentação em /docs:

Proíba Campos Extras de Formulários

Em alguns casos de uso especiais (provavelmente não muito comum), você pode desejar restringir os campos do formulário para aceitar apenas os declarados no modelo Pydantic. E proibir qualquer campo extra.

Nota

Isso é suportado desde a versão 0.114.0 do FastAPI. 🤓

Você pode utilizar a configuração de modelo do Pydantic para definir forbid para qualquer campo 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

Caso um cliente tente enviar informações adicionais, ele receberá um retorno de erro.

Por exemplo, se o cliente tentar enviar os campos de formulário:

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

Ele receberá um retorno de erro informando-o que o campo extra não é permitido:

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

Combine Modelos de Formulários

Você pode combinar vários modelos de formulários com parâmetros Form individuais.

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}

O cliente envia name, tags e token como campos de formulário. Cada modelo valida os campos do formulário de forma independente.

A configuração extra de cada modelo se aplica a todos os campos do formulário, incluindo campos e arquivos declarados em outros parâmetros ou dependências. Com extra="forbid", qualquer campo fora desse modelo é rejeitado. Com extra="allow", esses campos são incluídos como extras do modelo.

Se várias declarações usarem o mesmo nome de campo de formulário, cada uma validará o valor desse campo. Use tipos e restrições compatíveis para campos compartilhados, para que o esquema OpenAPI possa descrevê-los de forma consistente.

Modelos de Formulários Opcionais

Declare um modelo de formulário com | None e um valor padrão None para tornar o grupo opcional.

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}

Se nem name nem age forem enviados, profile será None. Se qualquer um deles for enviado, o FastAPI validará o modelo e exigirá ambos os campos. Um valor vazio enviado também conta como fornecimento de um campo.

Usar | None sem um valor padrão ainda torna o parâmetro obrigatório.

Campos extras seguem a configuração extra do modelo. Extras ignorados não fazem com que o modelo seja considerado presente.

Um modelo opcional que contenha apenas caixas de seleção desmarcadas é omitido. Envie um valor como enabled=false para fornecê-lo explicitamente.

Uniões Opcionais de Modelos de Formulários

Você também pode usar uma união opcional de modelos.

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

Se nenhum campo de formulário for enviado, contact será None. Envie kind=person com name, ou kind=company com company_name. O discriminador seleciona qual modelo validar.

Combine Uniões de Modelos de Formulários

Você pode combinar uniões com outros modelos de formulários, parâmetros Form individuais e parâmetros File. Cada união é validada de forma independente.

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}

Envie todos os campos no mesmo formulário. Por exemplo, envie contact_type=person, name=Rick, notification_type=email, email=rick@example.com e token=secret.

Omita os campos de notificação para deixar notification como None.

Modelos de Formulários e Arquivos

Você também pode combinar um modelo de formulário com parâmetros de arquivo.

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}

Aqui, o cliente envia o campo name e o arquivo avatar usando multipart/form-data.

Resumo

Você pode utilizar modelos Pydantic para declarar campos de formulários no FastAPI. 😎