Body - Fields
Learn to add validation and metadata to Pydantic model fields using Field(). Master field-level constraints, descriptions, and advanced validation.
🎯 What You'll Learn
- •Import and use Field from pydantic for model attributes
- •Add validation constraints to model fields (gt, max_length, etc.)
- •Include metadata like titles and descriptions for better documentation
- •Understand the relationship between Field, Query, Path, and Body
Body - Fields
🎯 What You'll Learn
This lesson follows the official FastAPI "Body - Fields" tutorial exactly. You'll learn how to use Pydantic's Field() function to add validation and metadata to model attributes, making your APIs more robust and well-documented.
By the end of this lesson, you'll understand how to:
- Import and use
Fieldfrom pydantic for model attributes - Add validation constraints to model fields (gt, max_length, etc.)
- Include metadata like titles and descriptions for better documentation
- Understand the relationship between Field, Query, Path, and Body
📚 Import Field
First, you need to import Field from pydantic:
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
⚠️ Important: Notice that Field is imported directly from pydantic, not from fastapi like Query, Path, and Body.
🔧 Declare Model Attributes with Field
You can use Field with model attributes to add validation and metadata:
class Item(BaseModel):
name: str
description: Union[str, None] = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: Union[float, None] = None
Field Parameters
Field works the same way as Query, Path, and Body - it accepts the same validation parameters:
- Validation constraints:
gt,ge,lt,le,min_length,max_length, etc. - Metadata:
title,description,example, etc. - Default values:
default,default_factory
🎯 Complete Example
Following the official FastAPI tutorial:
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item = Body(embed=True)):
results = {"item_id": item_id, "item": item}
return results
Request Body Structure
With Body(embed=True), the request expects:
{
"item": {
"name": "Laptop",
"description": "High-performance gaming laptop",
"price": 999.99,
"tax": 99.99
}
}
🌟 Key Concepts
Field vs Function Parameters
Notice the similarity between model attributes with Field and function parameters:
Model attribute with Field:
price: float = Field(gt=0, description="The price must be greater than zero")
Function parameter with Path:
item_id: int = Path(gt=0, description="The ID of the item")
Both follow the same pattern: parameter: type = ValidationFunction(constraints)
Validation Benefits
Field validation provides:
- Automatic validation: Invalid data returns HTTP 422 with detailed error messages
- Type conversion: Automatic conversion between compatible types
- Documentation: Metadata appears in OpenAPI schema and interactive docs
- IDE support: Better autocomplete and type checking
Technical Details
Query,Path,Bodycreate objects of subclasses ofParamclassParamis a subclass of Pydantic'sFieldInfoclassFieldreturns an instance ofFieldInfodirectly- All use the same underlying validation system
💡 Best Practices
Validation Constraints
class Item(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0, le=1000000) # Positive, reasonable max
quantity: int = Field(ge=1, le=1000) # At least 1, reasonable max
email: str = Field(regex=r'^[\w\.-]+@[\w\.-]+\.\w+$')
Descriptive Metadata
class User(BaseModel):
username: str = Field(
title="Username",
description="Unique identifier for the user",
min_length=3,
max_length=50,
example="johndoe"
)
age: int = Field(
title="Age",
description="Age in years",
ge=0,
le=150,
example=25
)
Optional Fields with Defaults
class Item(BaseModel):
name: str
description: Union[str, None] = Field(
default=None,
title="Item Description",
max_length=500
)
is_active: bool = Field(default=True, description="Whether item is active")
🔗 Add Extra Information
You can include additional metadata in Field that will appear in the generated JSON Schema:
class Item(BaseModel):
name: str = Field(
title="Item Name",
description="The name of the item",
example="Laptop"
)
price: float = Field(
gt=0,
description="Price in USD",
example=999.99
)
⚠️ Warning: Extra keys passed to Field will be present in the OpenAPI schema. Some OpenAPI tools may not work with non-standard keys.
🎯 Validation Examples
Valid Request
{
"item": {
"name": "Gaming Laptop",
"description": "High-performance laptop for gaming",
"price": 1299.99
}
}
✅ Result: Success - all validations pass
Invalid Requests
Negative price:
{
"item": {
"name": "Laptop",
"price": -100
}
}
❌ Result: HTTP 422 - "ensure this value is greater than 0"
Description too long:
{
"item": {
"name": "Laptop",
"description": "x".repeat(400), // Over 300 characters
"price": 999.99
}
}
❌ Result: HTTP 422 - "ensure this value has at most 300 characters"
🔗 What's Next?
In the next lesson, you'll learn about Body - Nested Models, where you'll discover how to create complex nested Pydantic models with lists, sets, and deeply nested structures.
📖 Additional Resources
💡 Hint
Import Field from pydantic (not fastapi). Use Field() to add validation like gt=0 for positive numbers and max_length for strings.
Ready to Practice?
Now that you understand the theory, let's put it into practice with hands-on coding!
Start Interactive Lesson