如何在Pydantic中分离Schema验证与逻辑/功能验证?
问题描述
我定义了如下User模型,其中包含Schema验证(如字段类型、EmailStr)及针对company字段的逻辑验证:
class User(BaseModel): name: str email: EmailStr phone_number: str job_title: str company: str salary: int @field_validator('company') def valid_company(cls, company: str) -> str: # validate that the company exists... return company
我希望让开发者能够分别进行Schema验证与逻辑验证,因此想到通过继承拆分模型:
class User(BaseModel): name: str email: EmailStr phone_number: str job_title: str company: str salary: int class UserValidator(User): @field_validator('company') def valid_company(cls, company: str) -> str: # validate that the company exists... return company
但User模型被其他模型(如Account)引用时,需要重写字段来替换为验证版模型:
class Account(BaseModel): user: User ... class AccountValidator(Account): user: UserValidator custom_validators...
请问是否有更优的实现方式?或可借助其他库来实现?
优化实现方案
方法1:使用Pydantic泛型模型
通过泛型让父模型动态引用不同的子模型,避免重复定义AccountValidator这类衍生模型,实现复用性:
from typing import Generic, TypeVar from pydantic import BaseModel, EmailStr, field_validator T = TypeVar('T') class User(BaseModel): name: str email: EmailStr phone_number: str job_title: str company: str salary: int class UserValidator(User): @field_validator('company') def valid_company(cls, company: str) -> str: # 模拟公司存在验证逻辑 if company not in ["Google", "Microsoft", "Apple"]: raise ValueError(f"Company {company} does not exist") return company class Account(BaseModel, Generic[T]): user: T account_id: str # 仅做Schema验证的Account类型 AccountSchema = Account[User] # 包含逻辑验证的Account类型 AccountWithValidation = Account[UserValidator]
开发者可根据需求直接使用AccountSchema或AccountWithValidation,无需重复编写模型结构。
方法2:通过验证器开关控制
在基础模型中添加可选的逻辑验证开关,通过model_config控制验证是否启用,无需拆分模型:
from pydantic import BaseModel, EmailStr, field_validator, ValidationInfo class User(BaseModel): name: str email: EmailStr phone_number: str job_title: str company: str salary: int model_config = {"validate_company": False} @field_validator('company') def valid_company(cls, company: str, info: ValidationInfo) -> str: if info.model_config.get("validate_company"): # 执行逻辑验证 if company not in ["Google", "Microsoft", "Apple"]: raise ValueError(f"Company {company} does not exist") return company # 仅执行Schema验证 user_schema = User( name="John", email="john@example.com", phone_number="123456", job_title="Engineer", company="Test", salary=100000 ) # 启用逻辑验证 user_with_validation = User( name="John", email="john@example.com", phone_number="123456", job_title="Engineer", company="Google", salary=100000, model_config={"validate_company": True} )
对于引用User的Account模型,同样可以通过model_config传递验证开关:
class Account(BaseModel): user: User account_id: str # 启用Account中user的逻辑验证 account_with_validation = Account( user=User( name="John", email="john@example.com", phone_number="123456", job_title="Engineer", company="Google", salary=100000 ), account_id="acc_123", model_config={"validate_company": True} )
方法3:分离逻辑验证到独立函数
把逻辑验证从模型中抽离为独立函数,开发者可在Schema验证完成后手动调用,实现完全的解耦:
from pydantic import BaseModel, EmailStr, ValidationError class User(BaseModel): name: str email: EmailStr phone_number: str job_title: str company: str salary: int def validate_user_company(user: User) -> User: # 逻辑验证逻辑 if user.company not in ["Google", "Microsoft", "Apple"]: raise ValueError(f"Company {user.company} does not exist") return user # 仅执行Schema验证 user_schema = User( name="John", email="john@example.com", phone_number="123456", job_title="Engineer", company="Test", salary=100000 ) # 额外执行逻辑验证 try: user_validated = validate_user_company(user_schema) except ValueError as e: print(e)
这种方式灵活性最高,适合在API请求处理等场景中分步执行验证流程。
内容的提问来源于stack exchange,提问作者Kevin Nammour
相关产品推荐
相关产品推荐

