如何扩展Pydantic现有类型(如FilePath)添加pattern验证与Schema支持
扩展Pydantic现有类型(如FilePath)并添加自定义Pattern验证
要在保留Pydantic原生类型(如FilePath)所有特性的前提下,添加可自定义的正则pattern验证及JSON Schema支持,可通过以下两种方案实现:
方案一:基于Annotated的便捷类型生成器
通过封装Annotated结合自定义验证器和动态Schema扩展,实现灵活的pattern复用,同时完全继承FilePath的存在性验证逻辑。
代码实现
import re from typing import Annotated from pydantic import BaseModel, FilePath, Field, AfterValidator from pydantic.json_schema import JsonSchemaValue # 生成路径pattern验证器 def validate_path_pattern(pattern: str): def validator(value: str) -> str: if not re.fullmatch(pattern, value): raise ValueError(f"路径不符合规则: {pattern}") return value return validator # 动态向JSON Schema添加pattern字段 def add_pattern_to_schema(pattern: str): def schema_extra(schema: JsonSchemaValue, _) -> None: # 在原有Schema基础上追加pattern,不覆盖原有字段(如format: path) schema['pattern'] = pattern return schema_extra # 生成带自定义pattern的FilePath类型别名 def FilePathWithPattern(pattern: str) -> type: return Annotated[ FilePath, AfterValidator(validate_path_pattern(pattern)), # 先验证路径存在,再检查pattern Field(json_schema_extra=add_pattern_to_schema(pattern)) ]
使用示例
class AppConfig(BaseModel): yaml_config: FilePathWithPattern(r".*\.yaml$") log_file: FilePathWithPattern(r".*\.log$") # 验证通过的情况 valid_model = AppConfig(yaml_config="/etc/app/config.yaml", log_file="/var/log/app.log") print(valid_model) # 验证失败的情况(路径存在但不符合pattern) try: invalid_model = AppConfig(yaml_config="/etc/app/config.txt", log_file="/var/log/app.log") except Exception as e: print(e) # 查看生成的JSON Schema(包含pattern字段及原生FilePath的format字段) print(AppConfig.model_json_schema())
方案二:泛型自定义类型
通过实现Pydantic的类型协议,创建可复用的泛型类型,写法更贴近原生类型系统。
代码实现
import re from typing import Generic, TypeVar, Any from pydantic import BaseModel, FilePath, GetCoreSchemaHandler, GetJsonSchemaHandler from pydantic_core import core_schema from pydantic.json_schema import JsonSchemaValue # 定义泛型参数,用于传递pattern字符串 PatternType = TypeVar('PatternType', bound=str) class FilePathPattern(Generic[PatternType]): @classmethod def __get_pydantic_core_schema__( cls, source_type: Any, handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: # 获取泛型参数中的pattern规则 pattern = source_type.__args__[0] # 获取原生FilePath的核心验证逻辑 file_path_schema = handler(FilePath) # 追加pattern验证逻辑 return core_schema.no_info_after_validator_function( lambda value: re.fullmatch(pattern, value) or ValueError(f"路径不符合规则: {pattern}"), file_path_schema ) @classmethod def __get_pydantic_json_schema__( cls, core_schema_: core_schema.CoreSchema, handler: GetJsonSchemaHandler ) -> JsonSchemaValue: # 获取原生FilePath的JSON Schema json_schema = handler(core_schema_) # 从泛型基类中提取pattern并添加到Schema pattern = cls.__orig_bases__[0].__args__[0] json_schema['pattern'] = pattern return json_schema
使用示例
class AppConfig(BaseModel): yaml_config: FilePathPattern[r".*\.yaml$"] log_file: FilePathPattern[r".*\.log$"] # 验证逻辑与方案一完全一致 valid_model = AppConfig(yaml_config="/etc/app/config.yaml", log_file="/var/log/app.log") print(valid_model)
核心问题解决说明
- Field的pattern不被FilePath识别:原生
FilePath未实现pattern验证逻辑,通过AfterValidator追加自定义验证器,实现路径存在性验证后的pattern检查。 - json_schema_extra写死导致复用性差:通过函数生成动态的
schema_extra或泛型参数传递pattern,实现同一类型的多场景复用。 - WithJsonSchema丢失原有关键字:两种方案均基于原生
FilePath的Schema进行修改(而非替换),保留了format: path等原生字段。
内容的提问来源于stack exchange,提问作者JE_Muc
相关产品推荐
相关产品推荐

