Pydantic中的枚举选择

2 投票
1 回答
66 浏览
提问于 2025-04-14 16:29

我有一个使用 pydantic(版本 2.6.1)创建的类,这个类里有一个属性,我想限制用户可以选择的选项。我可以用枚举(enum)来实现这个目的。但问题是,我有多个类需要设置不同的选择。下面是一个例子:

from enum import Enum
from pydantic import BaseModel, ValidationError, field_validator


class FruitEnum(str, Enum):
    pear = 'pear'
    banana = 'banana'
    grapes = 'grapes'


class CookingModel_1(BaseModel):
    fruit: FruitEnum

    @field_validator('fruit')
    def _validate_fruit(cls, value):
      if value not in (FruitEnum.pear,
                       FruitEnum.grapes):
        raise ValueError(f'{value} is not a allowed fruit.!')

class CookingModel_2(BaseModel):
    fruit: FruitEnum

    @field_validator('fruit')
    def _validate_fruit(cls, value):
      if value not in (FruitEnum.banana,
                       FruitEnum.grapes):
        raise ValueError(f'{value} is not a allowed fruit.!')

对于我的 CookingModel_1CookingModel_2 类,fruit 的选择是不同的。

我可以像上面那样来完成这个任务。但是如果我的烹饪模型列表很长(比如有 15 个),我就得在每个类里都定义一个验证器,这样会产生很多重复的代码,做的几乎是同样的事情。

基于这个想法,我可以定义一个嵌套函数,来创建一个参数化的字段验证器,像下面这样,从而减少重复的代码。

def get_fruit_validator(allowed_fruits):
  def _validator(fruit):
    if fruit not in allowed_fruits:
      raise ValueError(f'{fruit} is not a allowed fruit.!')
    return fruit
  return _validator


class FruitEnum(str, Enum):
    pear = 'pear'
    banana = 'banana'
    grapes = 'grapes'


class CookingModel_1(BaseModel):
    fruit: FruitEnum

    _fruit_validator = field_validator('fruit')(get_fruit_validator({FruitEnum.pear, FruitEnum.grapes}))

class CookingModel_2(BaseModel):
    fruit: FruitEnum

    _fruit_validator = field_validator('fruit')(get_fruit_validator({FruitEnum.banana, FruitEnum.grapes}))

这对我的使用场景来说已经足够好了。

不过,如果我有多个字段需要其他枚举和长列表,这样还是会导致代码重复。我在寻找一种更“优雅”的解决方案。也许可以引入一种新的约束类型(ConstraintType),来帮助限制选择?有没有其他不需要编写自定义验证器的替代方案呢?

1 个回答

1

感谢@yaakov-bressler的帮助,我用typing_extensions里的Literal解决了这个问题,下面是代码。

from typing_extensions import Literal

class CookingModel_1(BaseModel):
    fruit: Literal[FruitEnum.pear, FruitEnum.grapes]

class CookingModel_2(BaseModel):
    fruit: Literal[FruitEnum.banana, FruitEnum.grapes]

撰写回答