Agent skill
pydantic-models
Automatically applies when creating data models for API responses and validation. Uses Pydantic BaseModel with validators, field definitions, and proper serialization.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/pydantic-models
SKILL.md
Pydantic Model Pattern Enforcer
When creating data models for API responses, validation, or configuration, follow Pydantic best practices.
✅ Correct Pattern
python
from pydantic import BaseModel, Field, field_validator, model_validator
from typing import Optional, List
from datetime import datetime
class User(BaseModel):
"""User model with validation."""
id: str = Field(..., description="User unique identifier")
email: str = Field(..., description="User email address")
name: str = Field(..., min_length=1, max_length=100)
age: Optional[int] = Field(None, ge=0, le=150)
created_at: datetime = Field(default_factory=datetime.now)
tags: List[str] = Field(default_factory=list)
@field_validator('email')
@classmethod
def validate_email(cls, v: str) -> str:
"""Validate email format."""
if '@' not in v:
raise ValueError('Invalid email format')
return v.lower()
@field_validator('name')
@classmethod
def validate_name(cls, v: str) -> str:
"""Validate and clean name."""
return v.strip()
@model_validator(mode='after')
def validate_model(self) -> 'User':
"""Cross-field validation."""
if self.age and self.age < 13:
if '@' in self.email and not self.email.endswith('@parent.com'):
raise ValueError('Users under 13 must use parent email')
return self
class Config:
"""Pydantic configuration."""
json_schema_extra = {
"example": {
"id": "usr_123",
"email": "user@example.com",
"name": "John Doe",
"age": 30,
"tags": ["premium", "verified"]
}
}
Field Definitions
python
from pydantic import BaseModel, Field, HttpUrl, EmailStr
from typing import Annotated
from decimal import Decimal
class Product(BaseModel):
"""Product with comprehensive field validation."""
# Required field
name: str = Field(..., min_length=1, max_length=200)
# Optional with default
description: str = Field("", max_length=1000)
# Numeric constraints
price: Decimal = Field(..., ge=0, decimal_places=2)
quantity: int = Field(0, ge=0)
# String patterns
sku: str = Field(..., pattern=r'^[A-Z0-9-]+$')
# URL validation
image_url: Optional[HttpUrl] = None
# Email validation (requires email-validator package)
contact_email: Optional[EmailStr] = None
# Annotated types
weight: Annotated[float, Field(gt=0, description="Weight in kg")]
# Enum field
status: Literal["active", "inactive", "discontinued"] = "active"
Field Validators
python
from pydantic import BaseModel, field_validator
import re
class PhoneContact(BaseModel):
phone: str
country_code: str = "US"
@field_validator('phone')
@classmethod
def validate_phone(cls, v: str) -> str:
"""Validate and normalize phone number."""
# Remove non-digits
digits = re.sub(r'\D', '', v)
if len(digits) != 10:
raise ValueError('Phone must be 10 digits')
return digits
@field_validator('country_code')
@classmethod
def validate_country(cls, v: str) -> str:
"""Validate country code."""
allowed = ['US', 'CA', 'UK']
if v not in allowed:
raise ValueError(f'Country must be one of {allowed}')
return v
Model Validators
python
from pydantic import BaseModel, model_validator
from datetime import datetime
class Booking(BaseModel):
start_date: datetime
end_date: datetime
guests: int
@model_validator(mode='after')
def validate_dates(self) -> 'Booking':
"""Validate date range."""
if self.end_date <= self.start_date:
raise ValueError('end_date must be after start_date')
duration = (self.end_date - self.start_date).days
if duration > 30:
raise ValueError('Booking cannot exceed 30 days')
return self
@model_validator(mode='after')
def validate_capacity(self) -> 'Booking':
"""Validate guest count."""
if self.guests < 1:
raise ValueError('At least 1 guest required')
if self.guests > 10:
raise ValueError('Maximum 10 guests allowed')
return self
Nested Models
python
from pydantic import BaseModel
from typing import List
class Address(BaseModel):
"""Address component."""
street: str
city: str
state: str
zip_code: str
class Customer(BaseModel):
"""Customer with nested address."""
name: str
email: str
billing_address: Address
shipping_address: Optional[Address] = None
orders: List['Order'] = []
class Order(BaseModel):
"""Order model."""
id: str
total: Decimal
items: List['OrderItem']
class OrderItem(BaseModel):
"""Order item."""
product_id: str
quantity: int
price: Decimal
# Update forward references
Customer.model_rebuild()
Model Configuration
python
from pydantic import BaseModel, ConfigDict
class StrictModel(BaseModel):
"""Model with strict validation."""
model_config = ConfigDict(
# Validation settings
str_strip_whitespace=True, # Strip whitespace from strings
validate_assignment=True, # Validate on field assignment
validate_default=True, # Validate default values
strict=True, # Strict type checking
# Serialization settings
use_enum_values=True, # Serialize enums as values
populate_by_name=True, # Allow field population by alias
# Extra fields
extra='forbid', # Forbid extra fields (default: 'ignore')
# JSON schema
json_schema_extra={
"example": {...}
}
)
Computed Fields
python
from pydantic import BaseModel, computed_field
class Rectangle(BaseModel):
width: float
height: float
@computed_field
@property
def area(self) -> float:
"""Computed area."""
return self.width * self.height
@computed_field
@property
def perimeter(self) -> float:
"""Computed perimeter."""
return 2 * (self.width + self.height)
# Usage
rect = Rectangle(width=10, height=5)
print(rect.area) # 50.0
print(rect.perimeter) # 30.0
print(rect.model_dump()) # Includes computed fields
JSON Schema
python
from pydantic import BaseModel
class APIResponse(BaseModel):
"""API response model."""
data: dict
message: str
status: int = 200
# Generate JSON schema
schema = APIResponse.model_json_schema()
# Use in FastAPI (automatic)
@app.post("/api/endpoint")
async def endpoint(data: APIResponse) -> APIResponse:
return data
# Custom schema
class CustomModel(BaseModel):
name: str
model_config = {
"json_schema_extra": {
"examples": [
{"name": "Example 1"},
{"name": "Example 2"}
]
}
}
Serialization
python
from pydantic import BaseModel, field_serializer
from datetime import datetime
class Event(BaseModel):
name: str
timestamp: datetime
metadata: dict
@field_serializer('timestamp')
def serialize_timestamp(self, value: datetime) -> str:
"""Serialize timestamp as ISO string."""
return value.isoformat()
def model_dump_json(self, **kwargs) -> str:
"""Serialize to JSON string."""
return super().model_dump_json(**kwargs)
# Usage
event = Event(name="test", timestamp=datetime.now(), metadata={})
json_str = event.model_dump_json() # JSON string
dict_data = event.model_dump() # Python dict
❌ Anti-Patterns
python
# ❌ Using dict instead of model
def process_user(user: dict): # No validation!
pass
# ✅ Better: use Pydantic model
def process_user(user: User): # Validated!
pass
# ❌ Manual validation
def validate_email(email: str):
if '@' not in email:
raise ValueError("Invalid email")
# ✅ Better: use Pydantic validator
class User(BaseModel):
email: EmailStr # Built-in validation
# ❌ Mutable defaults
class Config(BaseModel):
tags: List[str] = [] # ❌ Shared across instances!
# ✅ Better: use Field with default_factory
class Config(BaseModel):
tags: List[str] = Field(default_factory=list)
# ❌ Not using Optional
class User(BaseModel):
middle_name: str # Required, but should be optional!
# ✅ Better
class User(BaseModel):
middle_name: Optional[str] = None
Best Practices Checklist
- ✅ Use
BaseModelfor all data models - ✅ Add field descriptions with
Field(..., description="...") - ✅ Use appropriate validators for business logic
- ✅ Use
Optionalfor nullable fields - ✅ Use
Field(default_factory=list)for mutable defaults - ✅ Add JSON schema examples
- ✅ Use computed fields for derived data
- ✅ Configure strict validation when needed
- ✅ Document models with docstrings
- ✅ Use nested models for complex structures
Auto-Apply
When creating models:
- Use
BaseModelas base class - Add type hints for all fields
- Use
Field()for constraints and descriptions - Add validators for business logic
- Add JSON schema examples
- Use nested models for complex data
Related Skills
- structured-errors - For error response models
- docstring-format - For model documentation
- pytest-patterns - For testing models
Didn't find tool you were looking for?