Last time, in Form Data, we read a login form with two separate Form() parameters. That works, but you already know where this goes. Just like query, cookie, and header params, you can bundle form fields into one Pydantic model and keep the signature tidy.
Bundling form fields into a model
Put the form fields in a Pydantic model, then declare the parameter with Form().
from typing import Annotatedfrom fastapi import FastAPI, Formfrom pydantic import BaseModelapp = FastAPI()class LoginForm(BaseModel): username: str password: str@app.post("/login")async def login(data: Annotated[LoginForm, Form()]): return {"username": data.username}Let's try to understand it:
LoginFormholds the fields, reusable on any endpoint that needs the same form.Annotated[LoginForm, Form()]tells FastAPI to read each field from form data, not JSON.FastAPI hands you back a ready
LoginFormobject. Access fields with a dot, likedata.username.
Same setup as before: forms need python-multipart installed. This model approach works since FastAPI 0.113.0.
Forbidding extra fields
Want to reject any field you did not declare? Forbid extras with Pydantic config.
class LoginForm(BaseModel): model_config = {"extra": "forbid"} username: str password: strNow if a client slips in an extra form field, they get a clean error instead of having it quietly ignored. Handy when you want tight control over exactly what the form accepts.
Request Files. Forms often carry more than text, think profile pictures and document uploads. Next we will handle file uploads with FastAPI's File and UploadFile. Meet you in next lesson.
