首页 > 编程问答 >为列表中的不同 JSON 模式模型定义约束

为列表中的不同 JSON 模式模型定义约束

时间:2024-07-30 04:31:33浏览次数:7  
标签:python json pydantic

我有一些 JSON,其结构类似于下图所示。 threshold 列表表示类型可以是 "type": "upper_limit" "type": "range" 的对象。请注意, "target" 值应该是整数或浮点数,具体取决于对象的类型。

{
    "name": "blah",
    "project": "blah blah",
    "threshold": [
        {
            "id": "234asdflkj",
            "group": "walkers",
            "type": "upper_limit",
            "target": 20,
            "var": "distance"
        },
        {
            "id": "asdf34asf2654",
            "group": "runners",
            "type": "range",
            "target": 1.7,
            "var": "speed"
        }
    ]
}

为上述数据生成 JSON 模式的 Pydantic 模型如下所示:

class ThresholdType(str, Enum):
    upper_limit = "upper_limit"
    range = "range"


class ThresholdUpperLimit(BaseModel):
    id: str
    group: str
    type: ThresholdType = "upper_limit"
    target: int = Field(gt=2, le=20)
    var: str


class ThresholdRange(BaseModel):
    id: str
    group: str
    type: ThresholdType = "range"
    target: float = Field(gt=0, lt=10)
    var: str


class Campaign(BaseModel):
    name: str
    project: str
    threshold: list[ThresholdUpperLimit | ThresholdRange]

模型验证 JSON,但有约束对于该类型, target 值将被忽略。例如,如果阈值对象包含 "type": "range", "target": 12, ,则不会抛出错误,因为它被解析为整数,因此使用 ThresholdUpperLimit 定义的约束;但应使用 ThresholdRange 定义的约束。关于如何正确处理这个问题有什么建议吗?


是对的,的 Pydantic 模型无法按预期捕获 target 字段的类型约束,因为它取决于 type 字段。这是因为在使用 Union 时,Pydantic 首先会尝试使用第一个匹配的模型(在本例中为 ThresholdUpperLimit ),而没有考虑类型约束。

可以通过以下几种方式解决此问题:

1. 使用自定义验证器和根验证器:

  • Union 中删除 type 字段,并在根验证器中进行处理。
  • 使用 root_validator 根据 type 字段的值对 target 字段应用验证逻辑。
from pydantic import BaseModel, Field, root_validator, ValidationError


class ThresholdType(str, Enum):
    upper_limit = "upper_limit"
    range = "range"


class ThresholdBase(BaseModel):
    id: str
    group: str
    type: ThresholdType
    target: Union[int, float]
    var: str

    @root_validator
    def validate_target_type(cls, values):
        target_type = values.get("type")
        target_value = values.get("target")
        if target_type == ThresholdType.upper_limit:
            if not isinstance(target_value, int) or not 2 < target_value <= 20:
                raise ValueError("target must be an integer between 2 and 20 for upper_limit type")
        elif target_type == ThresholdType.range:
            if not isinstance(target_value, float) or not 0 < target_value < 10:
                raise ValueError("target must be a float between 0 and 10 for range type")
        return values


class Campaign(BaseModel):
    name: str
    project: str
    threshold: list[ThresholdBase]

2. 使用 Discriminated Unions(Pydantic >= 1.0):

从 Pydantic 1.0 开始,可以使用 discriminator 字段创建区分联合。这允许 Pydantic 根据 type 字段的值选择正确的模型。

from pydantic import BaseModel, Field, discriminator


class ThresholdType(str, Enum):
    upper_limit = "upper_limit"
    range = "range"


class ThresholdBase(BaseModel):
    id: str
    group: str
    type: ThresholdType
    target: Union[int, float]
    var: str


class ThresholdUpperLimit(ThresholdBase):
    type: ThresholdType = "upper_limit"
    target: int = Field(gt=2, le=20)


class ThresholdRange(ThresholdBase):
    type: ThresholdType = "range"
    target: float = Field(gt=0, lt=10)


class Campaign(BaseModel):
    name: str
    project: str
    threshold: list[ThresholdBase] = Field(..., discriminator='type')

这两种方法都能正确验证 target 字段,具体取决于 type 字段的值。选择哪种方法取决于的偏好和 Pydantic 版本。

标签:python,json,pydantic
From: 78808926

相关文章

  • 如何在包含嵌套列表列表的 json 上使用 json_normalize
    我正在用Python处理一个复杂的JSON字符串,并且在将数据放入Pandas数据帧时遇到问题。示例数据、当前输出和预期输出全部如下。我正在尝试使用json_normalize,但它没有完全标准化,留下了几列包含JSON列表。我的最终目标是将其插入到SQLDB中,但是json字符串由于其长度而......
  • python - 面板库 - PasswordInput 不会对回车做出反应
    我试图避免需要提交按钮。以下代码当前正在远程jupyter实验室运行。仅当光标焦点从密码小部件中移除后,才会打印该消息。我想要回车来触发消息打印。有什么线索吗?frompanel.widgetsimportPasswordInput,TextInputpn.extension()defon_enter(event=None):message_p......
  • 即使安装了软件包,也找不到 python 模块句子转换器
    对于我的python脚本(如下所示),我使用句子转换器包,其中包含SBERT模型。即使在执行“piplist”时明确列出该软件包,并且还安装了torch,所有内容都更新到最新版本,但仍然找不到该模块。脚本:importnumpyasnpimportpandasaspdfromsentence_transformersimportSenten......
  • 有没有办法在 python 中返回类实例的布尔属性?
    我想组织我玩的游戏中的汽车数据。为此,我创建了一个“Car”类。我希望将此类实例的一些属性作为布尔值,这样我就可以将此类的所有实例放入列表中并过滤它们。我当前的解决方案不起作用,因为,我收到以下错误消息:TypeError:__repr__returnednon-string(typeCar)我使用......
  • python 正则表达式匹配一行中的多个单词而不转到下一行
    我正在编写一个解析器来解析以下输出:admin@str-s6000-on-5:~$showinterfacestatusEthernet4InterfaceLanesSpeedMTUAliasVlanOperAdminTypeAsymPFC------------------------------------------......
  • 使用 Python 平滑和对称不规则形状和曲线
    我需要完成三项任务:正则化曲线曲线的对称性完成不完整的曲线例如,这里是输入和预期的输出图像:输入输出|||在一般设置中,形状可以由任何SVG曲线基元(贝塞尔曲线、直线、弧线)表示。为了统一表示,示例包含曲线的折线近似。这些折线保存为......
  • 在Python中通过绝对路径引用数据文件夹
    我有一个大型python项目,其中数据太大,无法每次都以相同的方式共享。不同的人可能会使用网络位置或将某些内容复制到本地驱动器。该路径由不同子文件夹中的脚本和笔记本使用。例如,我将创建一个config.py来定义数据文件夹的路径。importsyssys.path.append('../'......
  • python三天速成记(看完你就会)day3 满满干货~
    续上文啦~EXCEL表的操作上一篇文章讲了怎么读取和操作txt和csv文档,但其实我们生活中还有一个常用的文本格式那就是excel文件,特别是在对大量数据进行处理的时候。excel文件的用处和广泛。其实在python中有很多库可以处理excel文件,但是本文主要介绍使用最实用最广泛的库pan......
  • Unity数据持久化——序列化/二进制/Json/PlayerPrefs
    序列化序列化是指把对象转换为字节序列的过程,而反序列化是指将字节序列恢复为对象的过程。序列化最主要的用途就是传递对象和保存对象。在Unity中保存和加载、prefab、Scene、Inspector窗口、实例化预制体等都使用了序列化和反序列化。下面是一个序列化和反序列化的例子......
  • Python输入验证改进的其他方式
    题目[题目来源:Python编程快速上手——让繁琐工作自动化(第二版)第三章实践项目,下面的解答程序为我自己完成的,仅供参考。]编写一个名为collatz()的函数,他有一个名为number的参数。如果参数是偶数,那么collatz()就输出number//2,并返回该值。如果参数是奇数,那么collatz()就......