What about the fields inside a Pydantic model? 🤔
Our Blog model has a title and content, but right now any string can sail through. An empty title? Fine. A 5000-character title? Also fine. We want rules on the individual fields, and we want them living right inside the model where they belong. That is exactly the job of Pydantic's Field.
Importing Field
Here is the one thing that trips people up, so let's get it out of the way first.
from pydantic import BaseModel, FieldLet's review:
Fieldcomes frompydantic, not fromfastapi. Notice the difference fromQuery,Path, andBody, which all come fromfastapi.It sits next to
BaseModelin the same import line, since both are Pydantic tools.
Mix this up and you will get an import error wondering where Field went. Keep it with Pydantic. 🙂
Adding rules to model fields
Now let's put Field to work on our Blog model.
from typing import Annotated, Optionalfrom fastapi import Body, FastAPIfrom pydantic import BaseModel, Fieldapp = FastAPI()class Blog(BaseModel): title: str = Field(min_length=3, max_length=100) content: str = Field(description="The full body of the blog post") rating: Optional[float] = Field(default=None, gt=0, le=5) is_active: bool = True@app.put("/blog/{blog_id}")async def update_blog(blog_id: int, blog: Annotated[Blog, Body(embed=True)]): return {"blog_id": blog_id, "blog": blog}Let's understand it:
titlenow must be between 3 and 100 characters. Send a one-letter title and FastAPI rejects it before your code runs.contentcarries adescription. That does not validate anything, it just enriches the auto-generated docs so the field explains itself.ratingis optional with a default ofNone, but if it is sent it must be greater than 0 and no more than 5. That is the samegtandlewe used withPathnumbers earlier.is_activestays a plain field with a default. Not every field needs aField. Use it only where you want extra rules or metadata.
If a client sends a rating of 9, FastAPI sends back a clean validation error pointing right at the rating field. No manual checks anywhere in your function.
Field is imported from pydantic, while Query, Path, and Body come from fastapi. They behave almost identically, but they live in different packages. When an import error shows up here, this swap is usually the reason. 😁 Been there, done that :(
