Dynamic Pydantic Model Patterns Framework
A comprehensive skill that enables aPI schema design with Pydantic models. Built for Claude Code with best practices and real-world patterns.
Pydantic Model Patterns Framework
Advanced Pydantic v2 modeling toolkit covering data validation, serialization, custom types, nested models, discriminated unions, and API schema generation for type-safe Python applications.
When to Use This Skill
Choose Pydantic Model Patterns when:
- Building data validation layers for APIs (FastAPI, Flask)
- Defining complex data models with nested structures
- Creating configuration management with validation
- Implementing serialization/deserialization pipelines
- Generating JSON Schema or OpenAPI specs from models
Consider alternatives when:
- Need ORM models — use SQLAlchemy or Tortoise ORM
- Simple data classes — use stdlib dataclasses for basic structures
- Need MongoDB schemas — use Beanie or Motor with Pydantic
Quick Start
# Activate Pydantic patterns claude skill activate dynamic-pydantic-model-patterns-framework # Design models claude "Create Pydantic models for an e-commerce order system" # Validate and transform claude "Add custom validation to the User model for email and password"
Example: Production Pydantic Models
from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict from datetime import datetime from enum import Enum from typing import Annotated class OrderStatus(str, Enum): PENDING = "pending" CONFIRMED = "confirmed" SHIPPED = "shipped" DELIVERED = "delivered" class Address(BaseModel): model_config = ConfigDict(str_strip_whitespace=True) street: str = Field(min_length=5, max_length=200) city: str = Field(min_length=2, max_length=100) state: str = Field(pattern=r'^[A-Z]{2}$') zip_code: str = Field(pattern=r'^\d{5}(-\d{4})?$') country: str = Field(default="US", pattern=r'^[A-Z]{2}$') class OrderItem(BaseModel): product_id: str quantity: int = Field(gt=0, le=100) unit_price: Annotated[float, Field(gt=0, decimal_places=2)] discount: float = Field(default=0, ge=0, le=1) @property def total(self) -> float: return round(self.quantity * self.unit_price * (1 - self.discount), 2) class Order(BaseModel): model_config = ConfigDict( str_strip_whitespace=True, validate_assignment=True, json_schema_extra={"examples": [{"customer_email": "[email protected]"}]}, ) id: str | None = None customer_email: str = Field(pattern=r'^[\w.-]+@[\w.-]+\.\w+$') items: list[OrderItem] = Field(min_length=1) shipping_address: Address status: OrderStatus = OrderStatus.PENDING created_at: datetime = Field(default_factory=datetime.utcnow) notes: str | None = Field(default=None, max_length=500) @field_validator('customer_email') @classmethod def normalize_email(cls, v: str) -> str: return v.lower() @model_validator(mode='after') def validate_order(self) -> 'Order': if self.total > 10000 and not self.notes: raise ValueError("Orders over $10,000 require notes") return self @property def total(self) -> float: return sum(item.total for item in self.items)
Core Concepts
Pydantic v2 Features
| Feature | Description | Example |
|---|---|---|
| Field Validation | Declarative constraints on fields | Field(gt=0, max_length=100) |
| Custom Validators | @field_validator and @model_validator | Complex business logic validation |
| Discriminated Unions | Type-safe union types with discriminator field | Annotated[Cat | Dog, Field(discriminator='type')] |
| Computed Fields | Cached computed properties | @computed_field |
| Serialization | Custom JSON serialization with model_dump | model_dump(exclude_none=True) |
| Generic Models | Type-parameterized models | class Response(BaseModel, Generic[T]) |
| Strict Mode | Disable type coercion | ConfigDict(strict=True) |
Model Design Patterns
| Pattern | Use Case | Example |
|---|---|---|
| Input/Output Models | Different shapes for create vs read | UserCreate / UserResponse |
| Base + Variants | Shared fields with specialization | BaseConfig → DevConfig, ProdConfig |
| Discriminated Unions | Polymorphic types | Payment = CreditCard | BankTransfer |
| Nested Models | Complex hierarchical data | Order → OrderItem → Product |
| Partial Models | Optional update schemas | All fields Optional for PATCH requests |
| Config Models | Application configuration | Settings(BaseSettings) with env vars |
# Discriminated Union pattern from typing import Literal, Annotated, Union from pydantic import BaseModel, Field class CreditCardPayment(BaseModel): type: Literal["credit_card"] = "credit_card" card_number: str = Field(pattern=r'^\d{16}$') expiry: str = Field(pattern=r'^\d{2}/\d{2}$') cvv: str = Field(pattern=r'^\d{3,4}$') class BankTransferPayment(BaseModel): type: Literal["bank_transfer"] = "bank_transfer" account_number: str routing_number: str = Field(pattern=r'^\d{9}$') Payment = Annotated[ Union[CreditCardPayment, BankTransferPayment], Field(discriminator='type') ] class Checkout(BaseModel): order_id: str payment: Payment # Automatically validates based on 'type' field
Configuration
| Parameter | Description | Default |
|---|---|---|
strict_mode | Disable automatic type coercion | false |
validate_assignment | Re-validate when attributes change | true |
str_strip_whitespace | Strip whitespace from strings | true |
frozen | Make models immutable | false |
json_schema_mode | Schema generation: validation, serialization | validation |
populate_by_name | Allow field population by alias or name | true |
Best Practices
-
Create separate models for input and output — Define
UserCreate(no id, required fields) andUserResponse(includes id, computed fields, excludes password). This prevents accidentally exposing internal fields and allows different validation for creation versus display. -
Use
Annotatedtypes for reusable field constraints — DefineEmail = Annotated[str, Field(pattern=r'^[\w.-]+@[\w.-]+\.\w+$')]once and reuse across models. This centralizes validation rules and ensures consistency. -
Prefer
model_validator(mode='after')for cross-field validation — After-mode validators receive the fully constructed model, making it easy to validate relationships between fields. Before-mode validators receive raw data and are harder to type safely. -
Use
ConfigDictinstead of innerclass Config— Pydantic v2 usesmodel_config = ConfigDict(...)as the modern configuration approach. The innerclass Configis deprecated and may be removed in future versions. -
Generate JSON Schema for API documentation — Use
model.model_json_schema()to generate JSON Schema from your models. This integrates automatically with FastAPI's OpenAPI docs and can be exported for external API documentation.
Common Issues
Pydantic v1 code breaks when upgrading to v2. Key changes: validator → field_validator, root_validator → model_validator, .dict() → .model_dump(), .json() → .model_dump_json(), class Config → model_config = ConfigDict(). Use pydantic.v1 compatibility module for gradual migration.
Validation error messages are not user-friendly for API responses. Pydantic's default ValidationError includes internal field paths and type names. Create a custom exception handler that transforms errors into user-friendly messages with the field name, constraint violated, and expected format.
Complex nested models are slow to validate for large payloads. Pydantic v2's Rust core is already 5-50x faster than v1. For extreme cases, use model_validate with strict=True to skip coercion, define __slots__ models, or validate only the fields that changed using partial validation patterns.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.