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

Pydantic模型导出:如何跳过默认值但保留指定字段?

Pydantic:强制保留特定默认字段在skip_defaults=True的输出中

问题场景

假设我们有如下Pydantic模型:

class A(BaseModel):
    a : int = 1
    b : int = 2  # 无论是否传入,始终返回`b`
    c : int = 3

需要调用inst.dict(skip_defaults=True)时,强制返回字段b(即使它是默认值),同时其他默认字段(如a、c)可以正常跳过。

用户尝试过以下无效方式:

  • dict(skip_defaults=True):正常跳过b,不符合需求
  • dict(skip_defaults=True, include={'b'}):include仅保留非默认值的字段,因此b仍被跳过
  • 在模型Config中设置include={"b"}:同样受skip_defaults规则限制,b作为默认值会被过滤

用户自行实现了重写dict方法的方案,但希望了解是否有更原生的Pydantic API实现方式:

class A(BaseModel):
    a : int = 1
    b : int = 2
    c : int = 3 
    def dict(self, *args, **kwargs):
        res = super(A,self).dict(*args, **kwargs)
        res.update(b=self.b)
        return res

实际业务场景

用Pydantic生成Tabulator.js的API参数:大部分字段匹配Tabulator的默认值,因此用skip_defaults避免重复默认值;但部分自定义默认值(如layout: "fitColumns")与Tabulator的默认值不同,必须强制返回该字段:

class TabulatorColumnOptions(BaseModel):
    title : str
    formatter : str = "string"
    headerFilter : str = ""
    sorter : str = ""

class TabulatorTableOptions(BaseModel):
    # 始终通过`dict()`返回
    layout : str = "fitColumns"      
    columns : list[TabulatorColumnOptions]

解决方案

1. Pydantic v1:使用Field(always=True)

这是最简洁的原生方案,给目标字段添加always=True参数,该字段会始终被包含在输出中,不受skip_defaults规则影响:

from pydantic import BaseModel, Field

class A(BaseModel):
    a: int = 1
    b: int = Field(2, always=True)  # 强制保留该字段
    c: int = 3

# 测试
inst = A()
print(inst.dict(skip_defaults=True))  # 输出: {'b': 2}
inst2 = A(b=5)
print(inst2.dict(skip_defaults=True))  # 输出: {'b': 5}

对应业务场景的模型修改:

class TabulatorTableOptions(BaseModel):
    layout: str = Field("fitColumns", always=True)  # 强制返回该字段
    columns: list[TabulatorColumnOptions]

2. Pydantic v2:自定义Dump方法

Pydantic v2移除了always=True参数,可通过自定义model_dump方法实现需求,同时保留灵活性:

from pydantic import BaseModel

class BaseWithForcedFields(BaseModel):
    # 子类需定义该集合,指定强制保留的字段
    _forced_fields = set()

    def model_dump(self, *args, exclude_unset=False, **kwargs):
        res = super().model_dump(*args, exclude_unset=exclude_unset, **kwargs)
        if exclude_unset:
            # 添加所有强制保留的字段
            for field in self._forced_fields:
                res[field] = getattr(self, field)
        return res

# 业务模型继承该基类
class TabulatorTableOptions(BaseWithForcedFields):
    _forced_fields = {'layout'}
    layout: str = "fitColumns"      
    columns: list[TabulatorColumnOptions]

# 测试
table_opts = TabulatorTableOptions(columns=[TabulatorColumnOptions(title="Name")])
print(table_opts.model_dump(exclude_unset=True))
# 输出: {'layout': 'fitColumns', 'columns': [{'title': 'Name'}]}

3. 优化版重写方法(通用兼容)

如果需要兼容不同版本的Pydantic,可以沿用用户的思路,但优化为通用基类,避免重复代码:

from pydantic import BaseModel

class BaseForcedFieldsModel(BaseModel):
    _forced_fields = set()

    def dict(self, *args, skip_defaults=False, **kwargs):
        res = super().dict(*args, skip_defaults=skip_defaults, **kwargs)
        if skip_defaults:
            for field in self._forced_fields:
                res[field] = getattr(self, field)
        return res

    # 兼容Pydantic v2的model_dump
    def model_dump(self, *args, exclude_unset=False, **kwargs):
        res = super().model_dump(*args, exclude_unset=exclude_unset, **kwargs)
        if exclude_unset:
            for field in self._forced_fields:
                res[field] = getattr(self, field)
        return res

# 使用示例
class A(BaseForcedFieldsModel):
    _forced_fields = {'b'}
    a: int = 1
    b: int = 2
    c: int = 3

总结

  • Pydantic v1优先使用Field(always=True),最符合原生API设计
  • Pydantic v2推荐通过自定义基类的方式实现,灵活且可复用
  • 用户自行重写dict/model_dump的方案完全可行,优化为通用基类后更适合多模型场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 06:20:58