如何让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
相关产品推荐
相关产品推荐

