重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
pydantic by manutej/luxor-claude-marketplace
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill pydantic您是一位专注于客户支持系统数据验证的 Pydantic 专家。您的职责是使用 Pydantic V2 帮助构建健壮、类型安全的数据模型,以验证支持工单、用户数据、API 请求和配置设置。
目的:创建具有自动类型强制转换和全面错误报告功能的数据验证模型。
关键原则:
model_dump() 和 model_dump_json() 进行序列化ValidationError 异常基本模式:
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:
# 记录验证错误供支持团队审查
for error in e.errors():
print(f"Field: {error['loc']}, Error: {error['msg']}")
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
目的:对单个字段应用细粒度的验证规则,以确保数据质量。
常见约束:
min_length、max_length、pattern、strip_whitespacegt、ge、lt、le、multiple_oftitle、description、examples、json_schema_extraalias、serialization_alias、exclude、include客户支持示例:
from pydantic import BaseModel, Field, EmailStr, HttpUrl
from typing import Annotated
from datetime import datetime
class CustomerProfile(BaseModel):
# 带约束的 ID 字段
customer_id: Annotated[int, Field(gt=0, description="唯一客户标识符")]
# 带验证的联系信息
email: EmailStr
phone: Annotated[str, Field(pattern=r'^\+?1?\d{9,15}$', description="国际电话格式")]
# 带长度约束的姓名字段
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_name: Optional[Annotated[str, Field(max_length=100)]] = None
company_website: Optional[HttpUrl] = None
# 带默认值的支持等级
support_tier: Annotated[str, Field(pattern=r'^(basic|premium|enterprise)$')] = 'basic'
# 元数据字段
registration_date: datetime
last_contact: Optional[datetime] = None
notes: str = Field(default='', max_length=2000, description="内部备注")
# 从序列化中排除(仅供内部使用)
internal_score: int = Field(default=0, exclude=True)
model_config = {
'str_strip_whitespace': True,
'validate_assignment': True,
'populate_by_name': True
}
目的:实现超出基本类型检查的业务逻辑验证。
@field_validator 模式:
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:
"""验证电子邮件格式并检查是否属于被阻止的域名"""
blocked_domains = ['tempmail.com', 'throwaway.email']
if '@' not in v:
raise ValueError('无效的电子邮件格式')
domain = v.split('@')[1].lower()
if domain in blocked_domains:
raise ValueError(f'不允许使用电子邮件域名 {domain}')
return v.lower()
@field_validator('subject')
@classmethod
def validate_subject(cls, v: str) -> str:
"""确保主题有意义且非垃圾信息"""
v = v.strip()
# 检查最小单词数
words = v.split()
if len(words) < 2:
raise ValueError('主题必须至少包含 2 个单词')
# 检查垃圾信息模式
spam_patterns = [r'viagra', r'casino', r'lottery']
for pattern in spam_patterns:
if re.search(pattern, v, re.IGNORECASE):
raise ValueError('主题包含禁止内容')
return v
@field_validator('attachments')
@classmethod
def validate_attachments(cls, v: list[str]) -> list[str]:
"""验证附件文件扩展名"""
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'不允许的文件类型 {ext}。允许的类型:{allowed_extensions}')
if len(v) > 5:
raise ValueError('最多允许 5 个附件')
return v
@field_validator('category')
@classmethod
def validate_category(cls, v: str) -> str:
"""规范化并验证工单类别"""
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'无效类别。有效选项:{valid_categories}')
return v_normalized
目的:验证多个字段之间的关系并执行跨字段验证。
@model_validator 模式:
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:
"""在字段验证前预处理和规范化数据"""
if isinstance(data, dict):
# 如果未提供,则自动生成估计小时数
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
# 规范化优先级
if 'priority' in data:
data['priority'] = data['priority'].lower()
return data
@model_validator(mode='after')
def validate_schedule(self) -> 'TicketSchedule':
"""在所有字段验证后验证调度逻辑"""
# 确保结束时间在开始时间之后
if self.scheduled_end <= self.scheduled_start:
raise ValueError('scheduled_end 必须在 scheduled_start 之后')
# 根据优先级验证持续时间
duration = (self.scheduled_end - self.scheduled_start).total_seconds() / 3600
if self.priority == 'urgent' and duration > 2:
raise ValueError('紧急工单必须在 2 小时内安排')
if self.priority == 'low' and duration < 1:
raise ValueError('低优先级工单需要至少分配 1 小时')
# 确保估计小时数与持续时间匹配
if abs(self.estimated_hours - duration) > 0.1:
raise ValueError('estimated_hours 必须与计划持续时间匹配')
# 验证工作时间(上午 9 点 - 下午 6 点)
if self.scheduled_start.hour < 9 or self.scheduled_start.hour >= 18:
raise ValueError('工单必须在工作时间(上午 9 点 - 下午 6 点)内安排')
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':
"""确保高级别升级需要批准"""
if self.escalation_level >= 3 and not self.approved_by:
raise ValueError('3 级及以上升级需要经理批准')
if self.approved_by == self.requested_by:
raise ValueError('批准人必须与请求人不同')
return self
目的:为复杂的支持工作流构建分层数据模型。
模式:
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) # 最大 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):
# 核心工单信息
ticket_id: int
customer: CustomerInfo
# 工单详情
subject: str = Field(min_length=5, max_length=200)
description: str = Field(min_length=20)
category: str
priority: str
status: TicketStatus
# 分配与跟踪
assigned_to: Optional[str] = None
created_at: datetime
updated_at: datetime
# 富内容
comments: list[Comment] = []
attachments: list[Attachment] = []
# 解决方案(如果已解决/关闭)
resolution: Optional[Resolution] = None
# 元数据
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':
"""确保工单状态一致"""
if self.status in (TicketStatus.RESOLVED, TicketStatus.CLOSED) and not self.resolution:
raise ValueError('已解决/关闭的工单必须包含解决方案详情')
if self.status == TicketStatus.IN_PROGRESS and not self.assigned_to:
raise ValueError('进行中的工单必须被分配')
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': '无法访问仪表板',
'description': '尝试访问分析仪表板时出现 403 错误',
'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'
}]
}
}
目的:创建具有自动验证和文档功能的类型安全 API 端点。
模式:
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field, ValidationError
from typing import Optional, List
from datetime import datetime
app = FastAPI(title="支持工单 API")
# 请求模型
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': '无法重置密码',
'description': '我点击了重置密码链接但没有收到电子邮件',
'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)$')
# 响应模型
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} # 用于 ORM 兼容性
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 端点
@app.post(
"/tickets/",
response_model=TicketResponse,
status_code=status.HTTP_201_CREATED,
responses={
400: {"model": ErrorResponse, "description": "验证错误"},
500: {"model": ErrorResponse, "description": "服务器错误"}
}
)
async def create_ticket(ticket: TicketCreateRequest):
"""创建具有自动验证功能的新支持工单"""
try:
# 业务逻辑在此处
# ticket_data 由 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
):
"""列出具有分页和筛选功能的工单"""
# 查询逻辑在此处
tickets = [] # 从数据库获取
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_data = update.model_dump(exclude_unset=True)
# 将更新应用到数据库
# 返回更新后的工单
pass
目的:通过验证从环境变量管理应用程序配置。
模式:
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, PostgresDsn, RedisDsn, EmailStr
from typing import Optional, List
class DatabaseSettings(BaseSettings):
"""数据库配置"""
host: str = Field(default='localhost', description='数据库主机')
port: int = Field(default=5432, ge=1, le=65535)
username: str
password: str
database: str = Field(default='support_db')
# 连接字符串的计算属性
@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 缓存配置"""
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):
"""电子邮件通知配置"""
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):
"""支持系统配置"""
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):
"""主应用程序设置"""
app_name: str = Field(default='支持工单系统')
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'])
# 嵌套设置
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'
)
# 用法
settings = ApplicationSettings()
print(f"连接到数据库:{settings.database.connection_url}")
print(f"每页最大工单数:{settings.support.max_tickets_per_page}")
目的:控制模型如何与字典、JSON 和其他格式相互转换。
模式:
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:
"""为导出中的隐私保护而屏蔽电子邮件"""
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:
"""为导出格式化日期时间"""
return dt.strftime('%Y-%m-%d %H:%M:%S')
@computed_field
@property
def days_open(self) -> int:
"""计算自工单创建以来的天数"""
return (datetime.now() - self.created_at).days
# 序列化模式
ticket = TicketExport(
ticket_id=123,
customer_email='john.doe@example.com',
subject='登录问题',
created_at=datetime.now(),
status='open',
priority='high',
internal_notes='客户致电两次'
)
# 标准序列化
print(ticket.model_dump())
# {'ticket_id': 123, 'customer_email': 'jo***e@example.com', ...}
# 包含所有字段(即使是排除的字段)
print(ticket.model_dump(mode='python', exclude_none=False))
# 序列化为 JSON
json_str = ticket.model_dump_json(indent=2)
print(json_str)
# 排除特定字段
print(ticket.model_dump(exclude={'internal_notes', 'assigned_to'}))
# 仅包含特定字段
print(ticket.model_dump(include={'ticket_id', 'subject', 'status'}))
目的:为复杂的业务需求实现复杂的验证逻辑。
模式:
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'无效等级。必须是以下之一:{valid_tiers}')
return v
@model_validator(mode='after')
def calculate_priority(self) -> 'TicketPrioritization':
"""基于多个因素自动计算优先级"""
priority_score = 0
# 客户等级权重
tier_weights = {'enterprise': 40, 'premium': 30, 'basic': 20, 'free': 10}
priority_score += tier_weights.get(self.customer_tier, 10)
# 业务影响权重
impact_weights = {'critical': 30, 'high': 20, 'medium': 10, 'low': 5}
priority_score += impact_weights.get(self.business_impact.lower(), 5)
# 受影响用户因素
if self.affected_users > 100:
priority_score += 20
elif self.affected_users > 10:
priority_score += 10
# 响应时间紧迫性
if self.response_time_hours <= 2:
priority_score += 10
# 存储计算出的优先级(您需要将此字段添加到模型中)
# self.computed_priority = 'urgent' if priority_score >= 80 else ...
return self
class SecureTicketData(BaseModel):
"""具有 PII 验证和清理功能的模型"""
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('信用卡后 4 位必须是恰好 4 位数字')
return v
@field_validator('phone')
@classmethod
def sanitize_phone(cls, v: Optional[str]) -> Optional[str]:
"""从电话中移除所有非数字字符"""
if v is None:
return v
digits_only = re.sub(r'\D', '', v)
if len(digits_only) < 10:
raise ValueError('电话号码必须至少有 10 位数字')
return digits_only
@model_validator(mode='after')
def validate_pii_consistency(self) -> 'SecureTicketData':
"""确保 PII 字段一致"""
# 如果提供了 SSN,则还应提供信用卡以进行身份验证
if self.ssn_last4 and not self.credit_card_last4:
raise ValueError('提供 SSN 时需要信用卡验证')
return self
目的:为高吞吐量支持系统优化验证性能。
关键策略:
from pydantic import TypeAdapter
from typing import List
# 定义一次适配器,重复用于验证
ticket_list_adapter = TypeAdapter(List[SupportTicket])
# 快速批量验证
tickets_data = [...] # 字典列表
validated_tickets = ticket_list_adapter.validate_python(tickets_data)
2. 利用严格模式提高性能:
from pydantic import BaseModel, ConfigDict
class FastTicket(BaseModel):
model_config = ConfigDict(strict=True) # 无类型强制转换
ticket_id: int # 必须是 int,不会从字符串强制转换
priority: str
3. 使用判别联合处理多态数据:
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
# 基于判别字段的快速分发
TicketUnion = Union[EmailTicket, PhoneTicket, ChatTicket]
class TicketProcessor(BaseModel):
ticket: TicketUnion = Field(discriminator='ticket_type')
4. 重用模型并避免动态创建:
# 良好:定义一次,重复使用
class TicketModel(BaseModel):
ticket_id: int
subject: str
# 避免:在循环中动态创建模型
for data in ticket_data:
# 不要动态创建模型
pass
from pydantic import ValidationError
import logging
logger = logging.getLogger(__name__)
def process_ticket_submission(data: dict) -> Optional[SupportTicket]:
"""处理工单并包含全面的错误处理"""
try:
ticket = SupportTicket(**data)
logger.info(f"工单 {ticket.ticket_id} 验证成功")
return ticket
except ValidationError as e:
# 记录详细的验证错误
logger.error(f"工单提交验证失败:{e.error_count()} 个错误")
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}':{message}(类型:{error_type})")
# 返回用户友好的错误响应
return None
except Exception as e:
logger.exception(f"处理工单时出现意外错误:{str(e)}")
return None
import pytest
from pydantic import ValidationError
def test_support_ticket_validation():
"""测试工单验证逻辑"""
# 有效工单
valid_data = {
'ticket_id': 123,
'customer_email': 'test@example.com',
'subject': '测试问题',
'description': '这是一个描述足够详细的测试工单',
'priority': 'medium',
'created_at': '2024-01-15T10:00:00'
}
ticket = SupportTicket(**valid_data)
assert ticket.ticket_id == 123
assert ticket.priority == 'medium'
# 无效优先级
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)
# 缺少必需字段
with pytest.raises(ValidationError):
incomplete_data = {k: v for k, v in valid_data.items() if k != 'subject'}
SupportTicket(**incomplete_data)
# 良好:为请求和响应使用单独的模型
class TicketCreateRequest(BaseModel):
subject: str
description: str
class TicketResponse(BaseModel):
ticket_id: int
subject: str
description: str
created_at: datetime
# 避免:对输入和输出使用相同的模型
# 良好:使用 default_factory
class Ticket(BaseModel):
tags: list[str] = Field(default_factory=list)
# 避免:可变默认值
class BadTicket(BaseModel):
tags: list[str] = [] # 在实例间共享!
# 良好:对派生值使用 computed_field
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
使用此技能时,请确保您:
每周安装次数
47
仓库
GitHub 星标数
46
首次出现
2026年1月22日
安全审计
安装于
opencode38
codex38
gemini-cli36
github-copilot34
claude-code33
cursor29
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.
Purpose : Create validated data models with automatic type coercion and comprehensive error reporting.
Key Principles :
model_dump() and model_dump_json() for serializationValidationError exceptions gracefullyBasic 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']}")
Purpose : Apply granular validation rules to individual fields for data quality assurance.
Common Constraints :
min_length, max_length, pattern, strip_whitespacegt, ge, lt, le, multiple_oftitle, description, , 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
}
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
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
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'
}]
}
}
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
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}")
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'}))
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
Purpose : Optimize validation performance for high-throughput support systems.
Key Strategies :
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)
2. 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
3. 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')
4. 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
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
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)
# 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
# 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!
# 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
When using this skill, ensure you:
Weekly Installs
47
Repository
GitHub Stars
46
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykFail
Installed on
opencode38
codex38
gemini-cli36
github-copilot34
claude-code33
cursor29
FastAPI官方技能:Python Web开发最佳实践与CLI工具使用指南
1,300 周安装
Symfony头脑风暴工具:优化架构与安全执行复杂变更的AI助手
204 周安装
TypeScript 最佳实践指南:常量类型、扁平化接口、工具类型与严格模式
206 周安装
营销分析师AI工具:营销活动绩效分析、归因建模与预算优化指南
206 周安装
get-available-resources:自动检测CPU/GPU/内存/磁盘资源,为科学计算提供策略建议
209 周安装
Figma MCP 集成指南:AI 驱动设计转代码,实现 React + Tailwind 精准开发
208 周安装
Proxmox VE 命令行管理指南:qm、pct 工具详解与虚拟机容器自动化
208 周安装
examplesjson_schema_extraalias, serialization_alias, exclude, include