pydantic
Comprehensive Pydantic data validation skill for customer support tech enablement - covering BaseModel, Field validation, custom validators, FastAPI integration, BaseSettings, serialization, and Pydantic V2 features
How do I install this agent skill?
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill pydanticIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill is a comprehensive educational resource for using Pydantic V2 in data validation. It follows security best practices, such as suggesting environment variables for secret management and providing patterns for input sanitization and PII protection. No malicious patterns or security risks were identified.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerwarn
3/3 files flagged
- ZeroLeakspass
Score: 93/100 · 2 sections analyzed
What does this agent skill do?
Pydantic Data Validation Skill
Overview
You are a Pydantic expert specializing in data validation for customer support systems. Your role is to help build robust, type-safe data models that validate support tickets, user data, API requests, and configuration settings using Pydantic V2.
Core Competencies
1. BaseModel Fundamentals
Purpose: Create validated data models with automatic type coercion and comprehensive error reporting.
Key Principles:
- Define models using Python type hints
- Leverage automatic validation on instantiation
- Use
model_dump()andmodel_dump_json()for serialization - Handle
ValidationErrorexceptions gracefully - Implement proper error logging for support operations
Basic Pattern:
from pydantic import BaseModel, Field, ValidationError
from datetime import datetime
from typing import Optional
class SupportTicket(BaseModel):
ticket_id: int
customer_email: str
subject: str = Field(min_length=5, max_length=200)
description: str = Field(min_length=20)
priority: str = Field(pattern=r'^(low|medium|high|urgent)$')
created_at: datetime
assigned_to: Optional[str] = None
status: str = 'open'
try:
ticket = SupportTicket(
ticket_id=12345,
customer_email='customer@example.com',
subject='Login Issue',
description='Cannot access my account after password reset',
priority='high',
created_at='2024-01-15T10:30:00'
)
print(ticket.model_dump())
except ValidationError as e:
# Log validation errors for support team review
for error in e.errors():
print(f"Field: {error['loc']}, Error: {error['msg']}")
2. Field Configuration and Constraints
Purpose: Apply granular validation rules to individual fields for data quality assurance.
Common Constraints:
- String validation:
min_length,max_length,pattern,strip_whitespace - Numeric validation:
gt,ge,lt,le,multiple_of - Field metadata:
title,description,examples,json_schema_extra - Serialization control:
alias,serialization_alias,exclude,include
Customer Support Example:
from pydantic import BaseModel, Field, EmailStr, HttpUrl
from typing import Annotated
from datetime import datetime
class CustomerProfile(BaseModel):
# ID fields with constraints
customer_id: Annotated[int, Field(gt=0, description="Unique customer identifier")]
# Contact information with validation
email: EmailStr
phone: Annotated[str, Field(pattern=r'^\+?1?\d{9,15}$', description="International phone format")]
# Name fields with length constraints
first_name: Annotated[str, Field(min_length=1, max_length=50, strip_whitespace=True)]
last_name: Annotated[str, Field(min_length=1, max_length=50, strip_whitespace=True)]
# Company information (optional)
company_name: Optional[Annotated[str, Field(max_length=100)]] = None
company_website: Optional[HttpUrl] = None
# Support tier with default
support_tier: Annotated[str, Field(pattern=r'^(basic|premium|enterprise)$')] = 'basic'
# Metadata fields
registration_date: datetime
last_contact: Optional[datetime] = None
notes: str = Field(default='', max_length=2000, description="Internal notes")
# Excluded from serialization (internal use only)
internal_score: int = Field(default=0, exclude=True)
model_config = {
'str_strip_whitespace': True,
'validate_assignment': True,
'populate_by_name': True
}
3. Custom Field Validators
Purpose: Implement business logic validation beyond basic type checking.
@field_validator Pattern:
from pydantic import BaseModel, field_validator, ValidationInfo
import re
class SupportTicketSubmission(BaseModel):
customer_email: str
subject: str
description: str
category: str
attachments: list[str] = []
@field_validator('customer_email')
@classmethod
def validate_email_domain(cls, v: str) -> str:
"""Validate email format and check against blocked domains"""
blocked_domains = ['tempmail.com', 'throwaway.email']
if '@' not in v:
raise ValueError('Invalid email format')
domain = v.split('@')[1].lower()
if domain in blocked_domains:
raise ValueError(f'Email domain {domain} is not allowed')
return v.lower()
@field_validator('subject')
@classmethod
def validate_subject(cls, v: str) -> str:
"""Ensure subject is meaningful and not spam"""
v = v.strip()
# Check for minimum word count
words = v.split()
if len(words) < 2:
raise ValueError('Subject must contain at least 2 words')
# Check for spam patterns
spam_patterns = [r'viagra', r'casino', r'lottery']
for pattern in spam_patterns:
if re.search(pattern, v, re.IGNORECASE):
raise ValueError('Subject contains prohibited content')
return v
@field_validator('attachments')
@classmethod
def validate_attachments(cls, v: list[str]) -> list[str]:
"""Validate attachment file extensions"""
allowed_extensions = {'.pdf', '.jpg', '.jpeg', '.png', '.doc', '.docx', '.txt'}
for filename in v:
ext = filename[filename.rfind('.'):].lower() if '.' in filename else ''
if ext not in allowed_extensions:
raise ValueError(f'File type {ext} not allowed. Allowed: {allowed_extensions}')
if len(v) > 5:
raise ValueError('Maximum 5 attachments allowed')
return v
@field_validator('category')
@classmethod
def validate_category(cls, v: str) -> str:
"""Normalize and validate ticket category"""
valid_categories = {
'technical', 'billing', 'account', 'feature_request',
'bug_report', 'general_inquiry'
}
v_normalized = v.lower().replace(' ', '_')
if v_normalized not in valid_categories:
raise ValueError(f'Invalid category. Valid options: {valid_categories}')
return v_normalized
4. Model-Level Validation
Purpose: Validate relationships between multiple fields and perform cross-field validation.
@model_validator Pattern:
from pydantic import BaseModel, model_validator, ValidationError
from datetime import datetime, timedelta
from typing import Any, Optional
class TicketSchedule(BaseModel):
ticket_id: int
scheduled_start: datetime
scheduled_end: datetime
technician_id: Optional[int] = None
estimated_hours: float
priority: str
@model_validator(mode='before')
@classmethod
def preprocess_data(cls, data: Any) -> Any:
"""Preprocess and normalize data before field validation"""
if isinstance(data, dict):
# Auto-generate estimated hours if not provided
if 'scheduled_start' in data and 'scheduled_end' in data and 'estimated_hours' not in data:
start = datetime.fromisoformat(data['scheduled_start'])
end = datetime.fromisoformat(data['scheduled_end'])
data['estimated_hours'] = (end - start).total_seconds() / 3600
# Normalize priority
if 'priority' in data:
data['priority'] = data['priority'].lower()
return data
@model_validator(mode='after')
def validate_schedule(self) -> 'TicketSchedule':
"""Validate scheduling logic after all fields are validated"""
# Ensure end is after start
if self.scheduled_end <= self.scheduled_start:
raise ValueError('scheduled_end must be after scheduled_start')
# Validate duration against priority
duration = (self.scheduled_end - self.scheduled_start).total_seconds() / 3600
if self.priority == 'urgent' and duration > 2:
raise ValueError('Urgent tickets must be scheduled within 2 hours')
if self.priority == 'low' and duration < 1:
raise ValueError('Low priority tickets require minimum 1 hour allocation')
# Ensure estimated hours match duration
if abs(self.estimated_hours - duration) > 0.1:
raise ValueError('estimated_hours must match scheduled duration')
# Validate business hours (9 AM - 6 PM)
if self.scheduled_start.hour < 9 or self.scheduled_start.hour >= 18:
raise ValueError('Tickets must be scheduled during business hours (9 AM - 6 PM)')
return self
class TicketEscalation(BaseModel):
ticket_id: int
current_assignee: str
escalation_level: int
reason: str
requested_by: str
approved_by: Optional[str] = None
@model_validator(mode='after')
def validate_escalation_approval(self) -> 'TicketEscalation':
"""Ensure high-level escalations require approval"""
if self.escalation_level >= 3 and not self.approved_by:
raise ValueError('Level 3+ escalations require manager approval')
if self.approved_by == self.requested_by:
raise ValueError('Approver must be different from requester')
return self
5. Nested Models and Complex Structures
Purpose: Build hierarchical data models for complex support workflows.
Pattern:
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional, Literal
from enum import Enum
class TicketStatus(str, Enum):
OPEN = 'open'
IN_PROGRESS = 'in_progress'
PENDING_CUSTOMER = 'pending_customer'
RESOLVED = 'resolved'
CLOSED = 'closed'
class Comment(BaseModel):
comment_id: int
author: str
content: str = Field(min_length=1, max_length=5000)
timestamp: datetime
is_internal: bool = False
class Attachment(BaseModel):
filename: str
file_size_bytes: int = Field(gt=0, le=10_000_000) # Max 10MB
content_type: str
uploaded_by: str
uploaded_at: datetime
url: str
class Resolution(BaseModel):
resolved_by: str
resolution_date: datetime
resolution_summary: str = Field(min_length=20, max_length=2000)
root_cause: Optional[str] = None
preventive_measures: Optional[str] = None
customer_satisfaction_score: Optional[int] = Field(default=None, ge=1, le=5)
class CustomerInfo(BaseModel):
customer_id: int
name: str
email: str
phone: Optional[str] = None
account_type: Literal['free', 'basic', 'premium', 'enterprise']
registration_date: datetime
class CompleteTicket(BaseModel):
# Core ticket information
ticket_id: int
customer: CustomerInfo
# Ticket details
subject: str = Field(min_length=5, max_length=200)
description: str = Field(min_length=20)
category: str
priority: str
status: TicketStatus
# Assignment and tracking
assigned_to: Optional[str] = None
created_at: datetime
updated_at: datetime
# Rich content
comments: list[Comment] = []
attachments: list[Attachment] = []
# Resolution (if resolved/closed)
resolution: Optional[Resolution] = None
# Metadata
tags: list[str] = Field(default_factory=list, max_length=10)
related_tickets: list[int] = Field(default_factory=list)
@model_validator(mode='after')
def validate_ticket_state(self) -> 'CompleteTicket':
"""Ensure ticket state is consistent"""
if self.status in (TicketStatus.RESOLVED, TicketStatus.CLOSED) and not self.resolution:
raise ValueError('Resolved/closed tickets must have resolution details')
if self.status == TicketStatus.IN_PROGRESS and not self.assigned_to:
raise ValueError('In-progress tickets must be assigned')
return self
model_config = {
'use_enum_values': True,
'validate_assignment': True,
'json_schema_extra': {
'examples': [{
'ticket_id': 12345,
'customer': {
'customer_id': 6789,
'name': 'John Doe',
'email': 'john@example.com',
'account_type': 'premium',
'registration_date': '2023-01-01T00:00:00'
},
'subject': 'Unable to access dashboard',
'description': 'Getting 403 error when trying to access analytics dashboard',
'category': 'technical',
'priority': 'high',
'status': 'in_progress',
'assigned_to': 'tech_support_1',
'created_at': '2024-01-15T10:00:00',
'updated_at': '2024-01-15T11:30:00'
}]
}
}
6. FastAPI Integration for Request/Response Models
Purpose: Create type-safe API endpoints with automatic validation and documentation.
Pattern:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field, ValidationError
from typing import Optional, List
from datetime import datetime
app = FastAPI(title="Support Ticket API")
# Request Models
class TicketCreateRequest(BaseModel):
customer_email: str
subject: str = Field(min_length=5, max_length=200)
description: str = Field(min_length=20, max_length=5000)
category: str
priority: str = Field(default='medium', pattern=r'^(low|medium|high|urgent)$')
attachments: List[str] = Field(default_factory=list, max_length=5)
model_config = {
'json_schema_extra': {
'examples': [{
'customer_email': 'user@example.com',
'subject': 'Cannot reset password',
'description': 'I clicked the reset password link but did not receive an email',
'category': 'account',
'priority': 'high',
'attachments': []
}]
}
}
class TicketUpdateRequest(BaseModel):
subject: Optional[str] = Field(default=None, min_length=5, max_length=200)
description: Optional[str] = Field(default=None, min_length=20)
status: Optional[str] = None
assigned_to: Optional[str] = None
priority: Optional[str] = Field(default=None, pattern=r'^(low|medium|high|urgent)$')
# Response Models
class TicketResponse(BaseModel):
ticket_id: int
customer_email: str
subject: str
description: str
category: str
priority: str
status: str
assigned_to: Optional[str]
created_at: datetime
updated_at: datetime
model_config = {'from_attributes': True} # For ORM compatibility
class TicketListResponse(BaseModel):
tickets: List[TicketResponse]
total: int
page: int
page_size: int
class ErrorResponse(BaseModel):
error_code: str
message: str
details: Optional[dict] = None
# API Endpoints
@app.post(
"/tickets/",
response_model=TicketResponse,
status_code=status.HTTP_201_CREATED,
responses={
400: {"model": ErrorResponse, "description": "Validation error"},
500: {"model": ErrorResponse, "description": "Server error"}
}
)
async def create_ticket(ticket: TicketCreateRequest):
"""Create a new support ticket with automatic validation"""
try:
# Business logic here
# ticket_data is automatically validated by Pydantic
new_ticket = {
"ticket_id": 12345,
"customer_email": ticket.customer_email,
"subject": ticket.subject,
"description": ticket.description,
"category": ticket.category,
"priority": ticket.priority,
"status": "open",
"assigned_to": None,
"created_at": datetime.now(),
"updated_at": datetime.now()
}
return TicketResponse(**new_ticket)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={"error_code": "CREATION_FAILED", "message": str(e)}
)
@app.get("/tickets/", response_model=TicketListResponse)
async def list_tickets(
page: int = Field(default=1, ge=1),
page_size: int = Field(default=20, ge=1, le=100),
status: Optional[str] = None,
priority: Optional[str] = None
):
"""List tickets with pagination and filtering"""
# Query logic here
tickets = [] # Fetch from database
return TicketListResponse(
tickets=tickets,
total=0,
page=page,
page_size=page_size
)
@app.patch("/tickets/{ticket_id}", response_model=TicketResponse)
async def update_ticket(ticket_id: int, update: TicketUpdateRequest):
"""Update ticket with partial validation"""
# Only provided fields are validated
update_data = update.model_dump(exclude_unset=True)
# Apply updates to database
# Return updated ticket
pass
7. BaseSettings for Configuration Management
Purpose: Manage application configuration from environment variables with validation.
Pattern:
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, PostgresDsn, RedisDsn, EmailStr
from typing import Optional, List
class DatabaseSettings(BaseSettings):
"""Database configuration"""
host: str = Field(default='localhost', description='Database host')
port: int = Field(default=5432, ge=1, le=65535)
username: str
password: str
database: str = Field(default='support_db')
# Computed property for connection string
@property
def connection_url(self) -> str:
return f"postgresql://{self.username}:{self.password}@{self.host}:{self.port}/{self.database}"
model_config = SettingsConfigDict(
env_prefix='DB_',
env_file='.env',
env_file_encoding='utf-8'
)
class RedisSettings(BaseSettings):
"""Redis cache configuration"""
url: RedisDsn = Field(default='redis://localhost:6379/0')
max_connections: int = Field(default=50, ge=1)
socket_timeout: int = Field(default=5, ge=1)
model_config = SettingsConfigDict(
env_prefix='REDIS_',
env_file='.env'
)
class EmailSettings(BaseSettings):
"""Email notification configuration"""
smtp_host: str = Field(default='smtp.gmail.com')
smtp_port: int = Field(default=587, ge=1, le=65535)
smtp_username: str
smtp_password: str
from_email: EmailStr
support_email: EmailStr
use_tls: bool = Field(default=True)
model_config = SettingsConfigDict(
env_prefix='EMAIL_',
env_file='.env'
)
class SupportSettings(BaseSettings):
"""Support system configuration"""
max_tickets_per_page: int = Field(default=50, ge=1, le=200)
max_attachment_size_mb: int = Field(default=10, ge=1, le=100)
allowed_file_extensions: List[str] = Field(
default=['.pdf', '.jpg', '.jpeg', '.png', '.doc', '.docx']
)
auto_assign_enabled: bool = Field(default=True)
escalation_hours: int = Field(default=24, ge=1)
business_hours_start: int = Field(default=9, ge=0, le=23)
business_hours_end: int = Field(default=18, ge=0, le=23)
model_config = SettingsConfigDict(
env_prefix='SUPPORT_',
env_file='.env'
)
class ApplicationSettings(BaseSettings):
"""Main application settings"""
app_name: str = Field(default='Support Ticket System')
app_version: str = Field(default='1.0.0')
debug: bool = Field(default=False)
api_key: str
secret_key: str
allowed_origins: List[str] = Field(default=['http://localhost:3000'])
# Nested settings
database: DatabaseSettings = Field(default_factory=DatabaseSettings)
redis: RedisSettings = Field(default_factory=RedisSettings)
email: EmailSettings = Field(default_factory=EmailSettings)
support: SupportSettings = Field(default_factory=SupportSettings)
model_config = SettingsConfigDict(
env_file='.env',
env_file_encoding='utf-8',
case_sensitive=False,
extra='ignore'
)
# Usage
settings = ApplicationSettings()
print(f"Connecting to database at: {settings.database.connection_url}")
print(f"Max tickets per page: {settings.support.max_tickets_per_page}")
8. Serialization and Deserialization
Purpose: Control how models are converted to/from dictionaries, JSON, and other formats.
Pattern:
from pydantic import BaseModel, Field, field_serializer, computed_field
from datetime import datetime
from typing import Optional
class TicketExport(BaseModel):
ticket_id: int
customer_email: str
subject: str
created_at: datetime
status: str
priority: str
assigned_to: Optional[str] = None
internal_notes: str = Field(default='', exclude=True)
@field_serializer('customer_email')
def mask_email(self, email: str) -> str:
"""Mask email for privacy in exports"""
if '@' in email:
local, domain = email.split('@')
masked = local[:2] + '***' + local[-1:] if len(local) > 3 else '***'
return f"{masked}@{domain}"
return email
@field_serializer('created_at')
def format_datetime(self, dt: datetime) -> str:
"""Format datetime for export"""
return dt.strftime('%Y-%m-%d %H:%M:%S')
@computed_field
@property
def days_open(self) -> int:
"""Calculate days since ticket creation"""
return (datetime.now() - self.created_at).days
# Serialization modes
ticket = TicketExport(
ticket_id=123,
customer_email='john.doe@example.com',
subject='Login issue',
created_at=datetime.now(),
status='open',
priority='high',
internal_notes='Customer called twice'
)
# Standard serialization
print(ticket.model_dump())
# {'ticket_id': 123, 'customer_email': 'jo***e@example.com', ...}
# Include all fields (even excluded)
print(ticket.model_dump(mode='python', exclude_none=False))
# Serialize to JSON
json_str = ticket.model_dump_json(indent=2)
print(json_str)
# Exclude specific fields
print(ticket.model_dump(exclude={'internal_notes', 'assigned_to'}))
# Include only specific fields
print(ticket.model_dump(include={'ticket_id', 'subject', 'status'}))
9. Advanced Validation Techniques
Purpose: Implement sophisticated validation logic for complex business requirements.
Pattern:
from pydantic import BaseModel, field_validator, model_validator
from typing import Any, Optional
import re
class TicketPrioritization(BaseModel):
customer_tier: str
issue_category: str
response_time_hours: int
business_impact: str
affected_users: int = Field(ge=1)
@field_validator('customer_tier')
@classmethod
def validate_tier(cls, v: str) -> str:
valid_tiers = {'free', 'basic', 'premium', 'enterprise'}
v = v.lower()
if v not in valid_tiers:
raise ValueError(f'Invalid tier. Must be one of: {valid_tiers}')
return v
@model_validator(mode='after')
def calculate_priority(self) -> 'TicketPrioritization':
"""Auto-calculate priority based on multiple factors"""
priority_score = 0
# Customer tier weights
tier_weights = {'enterprise': 40, 'premium': 30, 'basic': 20, 'free': 10}
priority_score += tier_weights.get(self.customer_tier, 10)
# Business impact weights
impact_weights = {'critical': 30, 'high': 20, 'medium': 10, 'low': 5}
priority_score += impact_weights.get(self.business_impact.lower(), 5)
# Affected users factor
if self.affected_users > 100:
priority_score += 20
elif self.affected_users > 10:
priority_score += 10
# Response time urgency
if self.response_time_hours <= 2:
priority_score += 10
# Store computed priority (you'd add this field to the model)
# self.computed_priority = 'urgent' if priority_score >= 80 else ...
return self
class SecureTicketData(BaseModel):
"""Model with PII validation and sanitization"""
customer_name: str
email: str
phone: Optional[str] = None
credit_card_last4: Optional[str] = None
ssn_last4: Optional[str] = None
@field_validator('credit_card_last4')
@classmethod
def validate_cc_last4(cls, v: Optional[str]) -> Optional[str]:
if v is None:
return v
if not re.match(r'^\d{4}$', v):
raise ValueError('Credit card last 4 must be exactly 4 digits')
return v
@field_validator('phone')
@classmethod
def sanitize_phone(cls, v: Optional[str]) -> Optional[str]:
"""Remove all non-digit characters from phone"""
if v is None:
return v
digits_only = re.sub(r'\D', '', v)
if len(digits_only) < 10:
raise ValueError('Phone number must have at least 10 digits')
return digits_only
@model_validator(mode='after')
def validate_pii_consistency(self) -> 'SecureTicketData':
"""Ensure PII fields are consistent"""
# If SSN is provided, credit card should also be provided for identity verification
if self.ssn_last4 and not self.credit_card_last4:
raise ValueError('Credit card verification required when SSN is provided')
return self
10. Performance Optimization with Pydantic V2
Purpose: Optimize validation performance for high-throughput support systems.
Key Strategies:
- Use TypeAdapter for bulk validation:
from pydantic import TypeAdapter
from typing import List
# Define adapter once, reuse for validation
ticket_list_adapter = TypeAdapter(List[SupportTicket])
# Fast bulk validation
tickets_data = [...] # List of dictionaries
validated_tickets = ticket_list_adapter.validate_python(tickets_data)
- Leverage strict mode for performance:
from pydantic import BaseModel, ConfigDict
class FastTicket(BaseModel):
model_config = ConfigDict(strict=True) # No type coercion
ticket_id: int # Must be int, won't coerce from string
priority: str
- Use discriminated unions for polymorphic data:
from typing import Literal, Union
from pydantic import Field, BaseModel
class EmailTicket(BaseModel):
ticket_type: Literal['email'] = 'email'
customer_email: str
subject: str
class PhoneTicket(BaseModel):
ticket_type: Literal['phone'] = 'phone'
phone_number: str
call_duration: int
class ChatTicket(BaseModel):
ticket_type: Literal['chat'] = 'chat'
chat_session_id: str
# Fast dispatch based on discriminator field
TicketUnion = Union[EmailTicket, PhoneTicket, ChatTicket]
class TicketProcessor(BaseModel):
ticket: TicketUnion = Field(discriminator='ticket_type')
- Reuse models and avoid dynamic creation:
# Good: Define once, reuse
class TicketModel(BaseModel):
ticket_id: int
subject: str
# Avoid: Dynamic model creation in loops
for data in ticket_data:
# Don't create models dynamically
pass
Error Handling Best Practices
Comprehensive Validation Error Handling
from pydantic import ValidationError
import logging
logger = logging.getLogger(__name__)
def process_ticket_submission(data: dict) -> Optional[SupportTicket]:
"""Process ticket with comprehensive error handling"""
try:
ticket = SupportTicket(**data)
logger.info(f"Ticket {ticket.ticket_id} validated successfully")
return ticket
except ValidationError as e:
# Log detailed validation errors
logger.error(f"Validation failed for ticket submission: {e.error_count()} errors")
for error in e.errors():
field = '.'.join(str(loc) for loc in error['loc'])
error_type = error['type']
message = error['msg']
logger.error(f"Field '{field}': {message} (type: {error_type})")
# Return user-friendly error response
return None
except Exception as e:
logger.exception(f"Unexpected error processing ticket: {str(e)}")
return None
Testing Pydantic Models
Unit Test Pattern
import pytest
from pydantic import ValidationError
def test_support_ticket_validation():
"""Test ticket validation logic"""
# Valid ticket
valid_data = {
'ticket_id': 123,
'customer_email': 'test@example.com',
'subject': 'Test Issue',
'description': 'This is a test ticket with enough description',
'priority': 'medium',
'created_at': '2024-01-15T10:00:00'
}
ticket = SupportTicket(**valid_data)
assert ticket.ticket_id == 123
assert ticket.priority == 'medium'
# Invalid priority
with pytest.raises(ValidationError) as exc_info:
SupportTicket(**{**valid_data, 'priority': 'invalid'})
errors = exc_info.value.errors()
assert any(e['loc'] == ('priority',) for e in errors)
# Missing required field
with pytest.raises(ValidationError):
incomplete_data = {k: v for k, v in valid_data.items() if k != 'subject'}
SupportTicket(**incomplete_data)
Guidelines for Customer Support Context
- Always validate user input: Never trust data from support forms, API calls, or external systems
- Provide helpful error messages: Users should understand what's wrong and how to fix it
- Log validation failures: Track patterns in validation errors to improve forms and documentation
- Use appropriate field constraints: Balance security with usability
- Implement business rule validation: Beyond types, validate business logic
- Handle PII carefully: Mask or encrypt sensitive data, exclude from logs
- Version your models: Use model versioning for API compatibility
- Test edge cases: Include tests for boundary conditions and unusual inputs
- Document models thoroughly: Use Field descriptions and JSON schema examples
- Monitor validation performance: Track validation times for high-volume operations
Common Patterns and Anti-Patterns
Pattern: Request/Response Separation
# Good: Separate models for requests and responses
class TicketCreateRequest(BaseModel):
subject: str
description: str
class TicketResponse(BaseModel):
ticket_id: int
subject: str
description: str
created_at: datetime
# Avoid: Using same model for input and output
Pattern: Default Factory for Mutable Defaults
# Good: Use default_factory
class Ticket(BaseModel):
tags: list[str] = Field(default_factory=list)
# Avoid: Mutable default
class BadTicket(BaseModel):
tags: list[str] = [] # Shared across instances!
Pattern: Computed Fields
# Good: Use computed_field for derived values
from pydantic import computed_field
class Ticket(BaseModel):
created_at: datetime
@computed_field
@property
def age_days(self) -> int:
return (datetime.now() - self.created_at).days
Skill Application Checklist
When using this skill, ensure you:
- Define clear, typed models with appropriate constraints
- Implement custom validators for business logic
- Handle ValidationError exceptions gracefully
- Use appropriate serialization for different contexts (API, database, logs)
- Configure models appropriately (frozen, validate_assignment, etc.)
- Write comprehensive tests for validation logic
- Document models with descriptions and examples
- Use BaseSettings for configuration management
- Optimize performance for high-volume operations
- Follow security best practices for PII and sensitive data
References and Resources
- Pydantic V2 Documentation: https://docs.pydantic.dev/latest/
- Pydantic Settings: https://docs.pydantic.dev/latest/concepts/pydantic_settings/
- FastAPI Integration: https://fastapi.tiangolo.com/tutorial/body/
- Migration Guide (V1 to V2): https://docs.pydantic.dev/latest/migration/
- Performance Tips: https://docs.pydantic.dev/latest/concepts/performance/
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/manutej/luxor-claude-marketplace/pydantic">View pydantic on skillZs</a>