You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现单个Pydantic字段的动态Float Enum名称校验及OpenAPI支持

解决方案:为特定Pydantic字段实现按名称校验的动态Enum

核心思路

要实现单个/多个字段按Enum名称校验,同时不影响全局Enum的默认行为,我们可以通过自定义校验器或扩展Enum类的方式,针对目标字段单独处理解析逻辑,同时满足动态生成Enum、序列化输出名称、OpenAPI Schema生成这三个核心需求。


方案一:使用BeforeValidator针对单个字段处理

适用于仅需给少数字段单独配置的场景,无需修改Enum本身。

步骤1:动态生成Enum

从运行时数据源(如配置、数据库)生成Enum:

from enum import Enum
from pydantic import BaseModel, BeforeValidator, ValidationInfo
from typing import Annotated

# 实际场景可替换为从外部加载的动态数据
factor_data = {"single": 1.0, "half": 0.4, "quarter": 0.1}
Factor = Enum("Factor", factor_data)

步骤2:编写名称解析校验器

创建校验器函数,将输入的名称转换为对应Enum实例:

def parse_enum_by_name(value, info: ValidationInfo):
    enum_type = info.field.annotation
    if isinstance(value, str):
        try:
            return enum_type[value]
        except KeyError:
            allowed_names = [member.name for member in enum_type]
            raise ValueError(f"无效名称: {value},可选值: {allowed_names}")
    # 若输入已是Enum实例,直接返回
    return value

步骤3:在模型中绑定校验器

用Annotated为目标字段单独配置校验逻辑,其他字段保持默认行为:

class Sex(str, Enum):
    MALE = "M"
    FEMALE = "F"

class Model(BaseModel):
    sex: Sex  # 保持默认按值校验逻辑
    # 为factor字段绑定按名称解析的校验器
    factor: Annotated[Factor, BeforeValidator(parse_enum_by_name)]

    class Config:
        # 序列化时输出Enum名称
        json_encoders = {Factor: lambda x: x.name}

测试验证

# 用名称传入可正常实例化
model = Model(sex="M", factor="half")
print(model.factor.value)  # 输出: 0.4
print(model.json())  # 输出: {"sex": "M", "factor": "half"}

# 传入错误名称会抛出明确提示
try:
    Model(sex="M", factor="invalid")
except ValueError as e:
    print(e)  # 输出: 无效名称: invalid,可选值: ['single', 'half', 'quarter']

方案二:自定义支持名称解析的Enum子类

若多个字段需要使用按名称校验的Enum,可自定义Enum基类,便于复用。

步骤1:自定义Enum基类

重写校验逻辑和Schema生成逻辑,确保支持名称解析且OpenAPI显示正确:

from enum import Enum
from pydantic import GetCoreSchemaHandler, ValidationInfo
from pydantic_core import core_schema

class NameResolvableEnum(Enum):
    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def validate(cls, value, info: ValidationInfo):
        if isinstance(value, str):
            try:
                return cls[value]
            except KeyError:
                allowed_names = [member.name for member in cls]
                raise ValueError(f"无效名称: {value},可选值: {allowed_names}")
        return super(cls, cls)._missing_(value)

    # 确保OpenAPI Schema显示Enum名称而非值
    @classmethod
    def __get_pydantic_core_schema__(cls, source_type, handler: GetCoreSchemaHandler):
        schema = handler(source_type)
        schema['enum'] = [member.name for member in cls]
        return schema

步骤2:动态生成Enum并继承基类

factor_data = {"single": 1.0, "half": 0.4, "quarter": 0.1}
# 动态生成Factor Enum,继承自定义基类
Factor = NameResolvableEnum("Factor", factor_data)

步骤3:在模型中直接使用

无需额外校验器,直接声明字段类型即可:

class Sex(str, Enum):
    MALE = "M"
    FEMALE = "F"

class Model(BaseModel):
    sex: Sex  # 保持默认行为
    factor: Factor  # 自动按名称校验

    class Config:
        json_encoders = {NameResolvableEnum: lambda x: x.name}

测试验证

效果与方案一一致,且OpenAPI Schema会自动生成["single", "half", "quarter"]作为枚举选项。


关键注意事项

  • 动态Enum扩展:两种方案都支持从外部数据源加载Enum键值对,只需替换factor_data即可。
  • Decimal兼容:后续若需将float改为Decimal,仅需修改factor_data中的值类型为Decimal("0.4"),逻辑无需额外调整。
  • Schema优化:方案一中若需生成正确的OpenAPI Schema,可给字段添加Field(enum=[member.name for member in Factor])。

内容的提问来源于stack exchange,提问作者NeilG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:52:48