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

如何让Pydantic对Dict类型字段的键值进行校验?

问题:Pydantic Dict字段内部修改时未触发类型校验

我定义了如下简单的数据模型:

from typing import Dict
from pydantic import BaseModel


class TableModel(BaseModel):
     table: Dict[str, str]

我想要像这样添加多个表项:

tables = TableModel(table={'T1': 'Tea'})
print(tables)  # table={'T1': 'Tea'}

tables.table['T2'] = 'coffee'
tables.table.update({'T3': 'Milk'})
print(tables)  # table={'T1': 'Tea', 'T2': 'coffee', 'T3': 'Milk'}

目前一切符合预期,但执行以下代码时并未报错:

tables.table[1] = 2
print(tables)  # table={'T1': 'Tea', 'T2': 'coffee', 'T3': 'Milk', 1: 2}

我曾将字段名改为__root__,也在模型Config中添加了validate_assignment = True,但问题依旧。如何让该模型对Dict字段的内部修改进行校验?是否遗漏了基础配置?


解决方案

Pydantic默认不会对嵌套的可变数据结构(如Dict、List)的内部修改做实时校验,因为直接操作这些对象时,本质是在修改Python原生数据类型,绕过了Pydantic的赋值校验机制。要解决这个问题,可以采用以下几种方式:

方法1:封装修改方法并主动触发校验

给模型添加自定义的修改方法,先校验键值类型,再修改字典,最后触发模型整体校验:

from typing import Dict
from pydantic import BaseModel, ValidationError

class TableModel(BaseModel):
    table: Dict[str, str]

    def add_table_item(self, key: str, value: str):
        # 先校验键值类型
        if not isinstance(key, str) or not isinstance(value, str):
            raise ValueError("键和值必须为字符串类型")
        self.table[key] = value
        # 触发模型整体校验,确保数据合规
        self.model_validate(self.model_dump())

# 使用示例
tables = TableModel(table={'T1': 'Tea'})
tables.add_table_item('T2', 'coffee')
print(tables)  # 输出:table={'T1': 'Tea', 'T2': 'coffee'}

try:
    tables.add_table_item(1, 2)
except ValueError as e:
    print(e)  # 输出:键和值必须为字符串类型

方法2:修改后手动触发模型校验

每次修改字典后,调用model_validate方法强制校验整个模型,不符合规则时会抛出ValidationError:

tables.table[1] = 2
try:
    # 将当前模型数据重新传入模型类进行校验
    tables = TableModel.model_validate(tables.model_dump())
except ValidationError as e:
    print(e)  # 抛出类型错误,提示键1不是字符串类型

方法3:添加字段校验器并重载索引赋值

给table字段添加校验器,同时重载模型的索引赋值方法,确保每次赋值都触发校验:

from typing import Dict
from pydantic import BaseModel, field_validator, ValidationError

class TableModel(BaseModel):
    table: Dict[str, str]

    @field_validator('table')
    def check_table_entries(cls, value):
        for key, val in value.items():
            if not isinstance(key, str):
                raise ValueError(f"键 {key} 必须为字符串")
            if not isinstance(val, str):
                raise ValueError(f"值 {val} 必须为字符串")
        return value

    def __setitem__(self, key: str, value: str):
        # 重载索引赋值操作,触发校验
        self.table[key] = value
        self.model_validate(self.model_dump())

# 使用示例
tables = TableModel(table={'T1': 'Tea'})
tables['T2'] = 'coffee'  # 正常执行
print(tables)

try:
    tables[1] = 2
except ValidationError as e:
    print(e)  # 抛出校验错误

核心原因

当直接操作tables.table时,你是在修改Python原生的dict对象,Pydantic的validate_assignment仅校验模型字段的整体赋值(比如tables.table = {"new": "dict"}),不会监听原生字典的内部修改。因此必须通过模型接口或手动触发校验的方式,才能确保字典的键值符合类型要求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 05:16:23