在Pydantic中从字典创建模型时不分配默认值的实现方案
实现方案
完全可行,通过自定义Pydantic基类,既能让模型实例仅保留传入的字段(不自动填充默认值),又能保证schema()方法生成的JSON Schema包含原始定义的默认值,同时支持嵌套模型。
核心思路
- 自定义基类重写数据预处理逻辑,仅保留传入的字段,跳过默认值自动填充
- 确保模型序列化时仅输出实际设置的字段
- 嵌套模型继承该基类,实现层级结构的一致行为
代码实现(Pydantic v2)
from pydantic import BaseModel, model_validator, Field from typing import Optional, Dict, Any # 自定义基类,实现无默认值填充的逻辑 class NoDefaultBaseModel(BaseModel): @model_validator(mode='before') def keep_only_provided_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]: # 直接返回传入的原始值,不触发默认值填充 return values def model_dump(self, **kwargs): # 默认仅序列化实际设置过的字段,和模型实例状态一致 return super().model_dump(exclude_unset=True, **kwargs) # 定义嵌套模型,继承自定义基类 class NestedModel(NoDefaultBaseModel): nested_field: Optional[str] = "default_nested" another_nested: Optional[int] = 123 # 定义主模型,继承自定义基类 class MyModel(NoDefaultBaseModel): my_field: Optional[str] = None my_other_field: Optional[str] = "aaaaaaaaaaab" nested: Optional[NestedModel] = Field(default_factory=lambda: NestedModel()) # 测试逻辑 my_dict = {"my_field": "value", "nested": {"nested_field": "custom_nested"}} my_model = MyModel(**my_dict) # 输出模型实例序列化结果:仅包含传入的字段 print(my_model.model_dump()) # 输出: {"my_field": "value", "nested": {"nested_field": "custom_nested"}} # 输出JSON Schema:保留所有字段的默认值定义 print(MyModel.schema()) # 输出示例(关键片段): # "my_other_field": {"title": "My Other Field", "type": "string", "default": "aaaaaaaaaaab"}, # "nested": {"$ref": "#/definitions/NestedModel", "default": {"nested_field": "default_nested", "another_nested": 123}}
关键点说明
- 数据预处理:
model_validator(mode='before')在验证前直接返回传入的原始字典,让Pydantic跳过默认值填充逻辑,仅对传入的字段做类型校验 - 序列化控制:重写
model_dump默认启用exclude_unset=True,确保输出和模型实例的实际字段一致 - 嵌套支持:所有嵌套模型必须继承
NoDefaultBaseModel,才能让嵌套层级也遵循相同的无默认值填充规则 - Schema保留默认值:
schema()方法基于模型类的原始字段定义生成,不受实例化逻辑的影响,因此默认值会被完整保留
兼容Pydantic v1的版本
如果使用Pydantic v1,只需将model_validator替换为root_validator(pre=True),model_dump替换为dict(exclude_unset=True):
from pydantic import BaseModel, root_validator, Field from typing import Optional, Dict, Any class NoDefaultBaseModel(BaseModel): @root_validator(pre=True) def keep_only_provided_fields(cls, values: Dict[str, Any]) -> Dict[str, Any]: return values def dict(self, **kwargs): return super().dict(exclude_unset=True, **kwargs) # 后续模型定义和测试逻辑一致
内容的提问来源于stack exchange,提问作者Stealth Rabbi
相关产品推荐
相关产品推荐

