FastAPI Model Fields
Introduction
When building APIs with FastAPI, Pydantic models serve as the backbone for request and response data validation. Pydantic's Field function provides powerful capabilities to customize how these models behave, enabling precise control over data validation, documentation, and transformation. In this guide, we'll explore how to effectively use model fields in FastAPI to create robust and well-documented APIs.
Understanding Pydantic Fields
In Pydantic models, each attribute is a "field" that comes with type hints to ensure data validation. The Field function extends this functionality with additional options that control validation rules, default values, and documentation.
Let's start with a basic example:
from pydantic import BaseModel, Field
class Product(BaseModel):
    id: int
    name: str = Field(..., description="The product name")
    price: float = Field(gt=0, description="Product price must be positive")
    is_available: bool = Field(default=True, description="Product availability status")
In this example:
- nameis a required field with a description for documentation
- pricemust be greater than zero
- is_availablehas a default value of- True
The ... (ellipsis) indicates that a field is required and has no default value.
Field Validation Constraints
Pydantic's Field provides numerous validation constraints for different data types:
Numeric Constraints
from pydantic import BaseModel, Field
class NumericExample(BaseModel):
    integer_field: int = Field(ge=1, le=100, description="Value between 1 and 100")
    float_field: float = Field(gt=0, lt=1.0, description="Value between 0 and 1.0 exclusive")
    multiple_of: int = Field(multiple_of=5, description="Must be a multiple of 5")
Here:
- ge: greater than or equal
- le: less than or equal
- gt: greater than
- lt: less than
- multiple_of: must be a multiple of the specified value
String Constraints
from pydantic import BaseModel, Field
class StringExample(BaseModel):
    username: str = Field(min_length=3, max_length=50)
    code: str = Field(regex="^[A-Z]{2}-[0-9]{4}$")  # Pattern like "AB-1234"
    long_text: str = Field(default="", title="Long Description", description="Optional extended description")
String validation includes:
- min_length: minimum string length
- max_length: maximum string length
- regex: regular expression pattern matching
Collection Constraints
from pydantic import BaseModel, Field
from typing import List, Set
class CollectionExample(BaseModel):
    tags: List[str] = Field(min_items=1, max_items=5)
    unique_codes: Set[str] = Field(default_factory=set)
    scores: List[int] = Field(min_items=3, max_items=10, gt=0, lt=100)
Collection constraints include:
- min_items: minimum number of items
- max_items: maximum number of items
- unique_items: whether all items must be unique (mostly for lists)
Fields with Default Values
Default values can be provided in several ways:
from pydantic import BaseModel, Field
from datetime import datetime
from typing import List
class DefaultsExample(BaseModel):
    created_at: datetime = Field(default_factory=datetime.now)
    tags: List[str] = Field(default_factory=list)
    status: str = Field(default="active")
    priority: int = Field(default=1)
Note that:
- defaultprovides a specific value
- default_factoryaccepts a function that returns a default value
- For mutable types like lists or dicts, use default_factoryto avoid shared references
Documentation Enhancement with Fields
One powerful aspect of Fields is their ability to enhance API documentation in FastAPI:
from pydantic import BaseModel, Field
from typing import Optional
class User(BaseModel):
    user_id: int = Field(..., description="Unique user identifier")
    username: str = Field(
        ..., 
        min_length=3,
        max_length=50,
        description="User's login name",
        examples=["john_doe", "jane_smith"]
    )
    email: str = Field(
        ...,
        description="User's email address",
        examples=["[email protected]"]
    )
    bio: Optional[str] = Field(
        None,
        title="Biography",
        description="User's optional biography",
        max_length=500
    )
When used in FastAPI, these fields produce rich API documentation:
- descriptionprovides explanations for each field
- examplessupplies sample values
- titleprovides an alternative display name
Advanced Field Usage
Field Aliases
Aliases let you use different names externally vs. internally:
from pydantic import BaseModel, Field
class UserModel(BaseModel):
    user_id: int = Field(..., alias="userId")
    is_active: bool = Field(..., alias="isActive")
# When creating from external data:
user = UserModel(userId=123, isActive=True)
print(user.user_id)  # 123
print(user.is_active)  # True
# When converting to JSON:
print(user.model_dump())  # {"userId": 123, "isActive": true}
Using Extra Field Metadata
You can add custom metadata to fields for your application's specific needs:
from pydantic import BaseModel, Field
class Product(BaseModel):
    name: str = Field(..., description="Product name", metadata={"searchable": True})
    internal_code: str = Field(..., metadata={"searchable": False, "internal_only": True})
    
    class Config:
        # Allow accessing custom metadata
        arbitrary_types_allowed = True
Frozen Fields
You can create read-only fields that can't be modified after model creation:
from pydantic import BaseModel, Field
class AuditLog(BaseModel):
    log_id: str = Field(..., frozen=True)
    timestamp: int = Field(..., frozen=True)
    message: str
Real-World Example: Product API
Let's see how we can apply field validation in a real-world FastAPI application:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from datetime import datetime
import uuid
app = FastAPI()
class ProductCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=100, description="Product name")
    description: Optional[str] = Field(None, max_length=1000)
    price: float = Field(..., gt=0, description="Product price - must be greater than zero")
    categories: List[str] = Field(default_factory=list, max_items=5)
    sku: Optional[str] = Field(None, regex=r"^[A-Z]{2}\d{6}$", description="Stock Keeping Unit, format: XX000000")
    
    @validator("categories")
    def categories_must_be_valid(cls, v):
        for category in v:
            if not category.strip():
                raise ValueError("Categories cannot be empty strings")
        return v
class Product(ProductCreate):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: Optional[datetime] = None
    in_stock: bool = Field(default=True)
# Database mock
products_db = {}
@app.post("/products/", response_model=Product)
def create_product(product: ProductCreate):
    product_dict = product.model_dump()
    product_id = str(uuid.uuid4())
    
    # Create complete product
    new_product = Product(
        id=product_id,
        **product_dict
    )
    
    # Store in mock database
    products_db[product_id] = new_product
    
    return new_product
@app.get("/products/{product_id}", response_model=Product)
def get_product(product_id: str):
    if product_id not in products_db:
        raise HTTPException(status_code=404, detail="Product not found")
    return products_db[product_id]
This example shows:
- Separate models for creation and representation
- Comprehensive field validation with custom validators
- Auto-generated fields using default factories
- Clear API documentation through field descriptions
Summary
Pydantic's Field function is a crucial tool for FastAPI developers that allows you to:
- Apply precise validation constraints to your data
- Provide default values in a clean way
- Enhance API documentation
- Control data serialization and deserialization
- Implement custom validation logic
By mastering Field usage, you'll create more robust APIs with better validation, clearer documentation, and improved developer experience.
Additional Resources
Exercises
- Create a UserRegistrationmodel with appropriate validation for username, email, and password fields.
- Build a BlogPostmodel with title, content, tags, and publication date fields, including proper validation.
- Implement a FastAPI endpoint that uses a Pydantic model with field constraints to validate and store a form submission.
- Create a model for financial transactions with proper numeric validation for amount, appropriate date fields, and status.
💡 Found a typo or mistake? Click "Edit this page" to suggest a correction. Your feedback is greatly appreciated!