← Back to Blog

Data Validation and Settings with Pydantic

Exploring the power of Pydantic for robust data validation and type-safe configuration in Python. Learn how to define models with type hints, add custom validators, manage app settings from the environment, and integrate Pydantic with FastAPI for request and response validation.

Data Validation and Settings with Pydantic

Pydantic is the go-to library for data validation and settings management using Python type hints. It powers FastAPI's request and response handling and is widely used across the Python ecosystem. In this guide, I'll walk you through defining models, validating input, managing configuration, and integrating Pydantic with your APIs and applications.

Why Pydantic?

Pydantic brings several benefits to Python projects:

  • Type-based validation: Validate data using standard type hints
  • Clear errors: Detailed validation errors with context
  • Serialization: Easy conversion to and from JSON/dict
  • Settings: Type-safe configuration from environment variables
  • IDE support: Great autocomplete and type checking

Installation and Basic Models

Install Pydantic and define your first models:

pip install pydantic

from pydantic import BaseModel
from typing import Optional

class User(BaseModel):
    id: int
    name: str
    email: str
    age: Optional[int] = None
    is_active: bool = True

# Validation happens automatically
user = User(id=1, name="Alice", email="alice@example.com")
print(user.model_dump())  # Serialize to dict
print(user.model_dump_json())  # Serialize to JSON

Validation and Custom Validators

Use built-in types and validators to enforce rules:

from pydantic import BaseModel, field_validator
from typing import Optional

class Product(BaseModel):
    name: str
    price: float
    sku: str

    @field_validator('price')
    @classmethod
    def price_must_be_positive(cls, v: float) -> float:
        if v <= 0:
            raise ValueError('price must be positive')
        return v

    @field_validator('sku')
    @classmethod
    def sku_uppercase(cls, v: str) -> str:
        return v.upper()

# Valid data
product = Product(name="Widget", price=9.99, sku="abc-123")
# Invalid data raises ValidationError with clear messages

Settings Management

Pydantic Settings lets you load configuration from the environment with validation:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "My App"
    debug: bool = False
    database_url: str
    api_key: str | None = None

    model_config = {
        "env_file": ".env",
        "env_file_encoding": "utf-8",
        "extra": "ignore"
    }

settings = Settings()  # Loads from environment and .env
print(settings.database_url)

Working with FastAPI

Pydantic and FastAPI work together seamlessly for request and response models:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class ItemCreate(BaseModel):
    name: str
    description: str | None = None
    price: float

class ItemResponse(BaseModel):
    id: int
    name: str
    description: str | None
    price: float

@app.post("/items/", response_model=ItemResponse)
async def create_item(item: ItemCreate):
    # item is already validated by Pydantic
    return ItemResponse(id=1, **item.model_dump())

Advanced Features

Pydantic supports nested models, discriminators, and more:

  • Nested models: Compose complex structures from smaller models
  • model_validator: Validate the whole model after fields are set
  • Computed fields: Add derived attributes with @computed_field
  • JSON Schema: Generate OpenAPI/JSON Schema for documentation

Best Practices

Tips for using Pydantic effectively:

  • Use separate models for input, internal state, and output when they differ
  • Keep validators focused and reuse them with custom types
  • Use Settings for all configuration; avoid ad-hoc env reads
  • Rely on model_dump() and model_dump_json() for serialization

Conclusion

Pydantic makes data validation and configuration in Python straightforward and type-safe. Whether you're building APIs with FastAPI or any application that needs validated input and settings, Pydantic is a powerful choice.

Start with simple BaseModel classes and add validators and Settings as needed. Your future self will thank you for clear error messages and fewer runtime bugs.