Skip to main content

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:

python
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:

  • name is a required field with a description for documentation
  • price must be greater than zero
  • is_available has 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

python
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

python
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

python
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:

python
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:

  • default provides a specific value
  • default_factory accepts a function that returns a default value
  • For mutable types like lists or dicts, use default_factory to avoid shared references

Documentation Enhancement with Fields

One powerful aspect of Fields is their ability to enhance API documentation in FastAPI:

python
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:

  • description provides explanations for each field
  • examples supplies sample values
  • title provides an alternative display name

Advanced Field Usage

Field Aliases

Aliases let you use different names externally vs. internally:

python
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:

python
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:

python
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:

python
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:

  1. Separate models for creation and representation
  2. Comprehensive field validation with custom validators
  3. Auto-generated fields using default factories
  4. 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

  1. Create a UserRegistration model with appropriate validation for username, email, and password fields.
  2. Build a BlogPost model with title, content, tags, and publication date fields, including proper validation.
  3. Implement a FastAPI endpoint that uses a Pydantic model with field constraints to validate and store a form submission.
  4. Create a model for financial transactions with proper numeric validation for amount, appropriate date fields, and status.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)