如何为List[List[int]]构建Pydantic模型及解决验证报错
问题描述
我是Pydantic新手,想要验证List[List[int]]类型的数据,编写了如下测试代码:
from pydantic import BaseModel, field_validator from typing import List, Optional class Scenes(BaseModel): scenes_start_duration_ms: List[List[int]] class Video(BaseModel): video_id: str scenes_start_duration_ms: Optional[Scenes] = None if __name__ == "__main__": response = { "scenes_start_duration_ms": [ [0, 1234], [1568, 2769], [4671, 734], [5405, 1268], ], "video_id": "kl", } m = Scenes.model_validate(response) print(m) m = Video.model_validate(response) print(m)
运行后输出:
scenes_start_duration_ms=[[0, 1234], [1568, 2769], [4671, 734], [5405, 1268]] Traceback (most recent call last): File "/media/hdd/P/UW/clipmaker/test-list.py", line 30, in <module> m = Video.model_validate(response) File "/media/hdd/P/UW/clipmaker/venv/lib/python3.10/site-packages/pydantic/main.py", line 509, in model_validate return cls.__pydantic_validator__.validate_python( pydantic_core._pydantic_core.ValidationError: 1 validation error for Video scenes_start_duration_ms Input should be a valid dictionary or instance of Scenes [type=model_type, input_value=[[0, 1234], [1568, 2769],...671, 734], [5405, 1268]], input_type=list]
我无法理解问题所在,后来将Video类中的代码修改为:
scenes_start_duration_ms: Optional[List[List[int]]] = None
后可以正常运行,但希望得到最优解决方案。
问题原因
Video类中scenes_start_duration_ms字段定义的类型是Optional[Scenes],但传入的response里该字段的值是List[List[int]]类型的二维列表,而非符合Scenes模型结构的字典(即{"scenes_start_duration_ms": [[...]]})。Pydantic验证时会期望该字段是Scenes实例或其对应的字典结构,因此抛出验证错误。
最优解决方案
根据需求场景,有两种合理的最优方案:
方案1:复用Scenes模型(适合需要分层封装的场景)
如果需要保留Scenes模型来封装场景数据的后续验证逻辑,可选择以下两种方式:
方式一:调整输入数据结构(推荐,若数据格式可修改)
将response中的scenes_start_duration_ms改为嵌套字典,匹配Scenes模型的结构:
response = { "scenes_start_duration_ms": { "scenes_start_duration_ms": [ [0, 1234], [1568, 2769], [4671, 734], [5405, 1268], ] }, "video_id": "kl", }
修改后Video.model_validate(response)即可正常运行。
方式二:添加字段解析逻辑(若无法修改输入数据)
使用field_validator在验证前将二维列表转换为Scenes模型所需的结构:
from pydantic import BaseModel, field_validator from typing import List, Optional, Any class Scenes(BaseModel): scenes_start_duration_ms: List[List[int]] class Video(BaseModel): video_id: str scenes_start_duration_ms: Optional[Scenes] = None @field_validator("scenes_start_duration_ms", mode="before") def parse_scenes(cls, v: Any) -> Any: if isinstance(v, list): return {"scenes_start_duration_ms": v} return v
这样即使传入的是二维列表,验证器会先将其转换为符合Scenes模型的字典,再完成验证。
方案2:直接使用List[List[int]](简洁高效)
如果不需要单独的Scenes模型做额外封装,直接将Video类的字段类型定义为Optional[List[List[int]]]就是最优解,即你更新后的代码:
class Video(BaseModel): video_id: str scenes_start_duration_ms: Optional[List[List[int]]] = None
该方案无需多余模型嵌套,代码简洁,适合不需要对场景数据做额外验证逻辑的场景。
内容的提问来源于stack exchange,提问作者Andrey Dmitriev

