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

如何在Pydantic深度嵌套结构中动态生成可选字段?

动态生成Pydantic嵌套模型对象(按需包含/省略可选字段)

问题场景

在集成测试中使用Pydantic构建测试对象时,通过default_factory自动填充必填字段,但面对深度嵌套的复杂模型,手动控制每个嵌套对象的可选字段(按需包含或省略)非常繁琐。示例代码:

from pydantic import BaseModel, Field
from faker import Faker

faker = Faker()

class Foo3(BaseModel):
    name: str = Field(default_factory=faker.sentence)
    city: str = None  # city为可选字段,但实际取值不能为None,需按需生成

需求:无需手动遍历每个嵌套模型的可选字段,自动生成必填字段的同时,灵活控制是否包含可选字段。


最佳解决方案

方案1:自定义递归生成函数(无第三方依赖)

核心思路是利用Pydantic模型的model_fields属性,自动区分必填/可选字段,对嵌套模型递归处理,按需生成数据。

from pydantic import BaseModel, Field
from faker import Faker
from typing import Type, Any

faker = Faker()

# 示例嵌套模型
class Foo3(BaseModel):
    name: str = Field(default_factory=faker.sentence)
    city: str | None = None  # 用Union类型规范可选字段定义

class Bar(BaseModel):
    id: int = Field(default_factory=faker.random_int)
    foo: Foo3
    zipcode: str | None = None

class Baz(BaseModel):
    code: str = Field(default_factory=faker.uuid4)
    bar: Bar
    region: str | None = None

def generate_test_model(model: Type[BaseModel], include_optional: bool = False) -> BaseModel:
    """
    递归生成Pydantic模型实例:自动填充必填字段,可选字段按需包含
    :param model: 目标Pydantic模型类
    :param include_optional: 是否生成并包含可选字段
    :return: 模型实例
    """
    instance_data = {}
    
    for field_name, field in model.model_fields.items():
        # 处理必填字段
        if field.is_required():
            # 嵌套模型递归生成
            if isinstance(field.annotation, type) and issubclass(field.annotation, BaseModel):
                instance_data[field_name] = generate_test_model(field.annotation, include_optional)
            else:
                # 优先使用字段自带的default_factory,否则按类型生成随机值
                if field.default_factory is not None:
                    instance_data[field_name] = field.default_factory()
                else:
                    # 可根据需求扩展更多类型的生成逻辑
                    if field.annotation is str:
                        instance_data[field_name] = faker.word()
                    elif field.annotation is int:
                        instance_data[field_name] = faker.random_int()
        # 处理可选字段(按需生成)
        elif include_optional:
            if isinstance(field.annotation, type) and issubclass(field.annotation, BaseModel):
                instance_data[field_name] = generate_test_model(field.annotation, include_optional)
            else:
                if field.default_factory is not None:
                    instance_data[field_name] = field.default_factory()
                else:
                    # 可选字段的随机值生成逻辑可自定义
                    if field.annotation is str:
                        instance_data[field_name] = faker.city() if field_name == "city" else faker.postcode()
                    elif field.annotation is int:
                        instance_data[field_name] = faker.random_int(min=1000, max=9999)
    
    return model(**instance_data)

# 使用示例
# 仅生成必填字段
baz_required = generate_test_model(Baz)
print(baz_required.model_dump())

# 包含所有可选字段
baz_full = generate_test_model(Baz, include_optional=True)
print(baz_full.model_dump())

方案2:使用第三方库pydantic-factories(更简洁)

如果允许引入第三方库,pydantic-factories专门针对Pydantic模型的测试数据生成,支持嵌套模型和灵活的字段排除/包含。

安装:

pip install pydantic-factories

使用示例:

from pydantic_factories import ModelFactory

# 为每个模型定义工厂类
class Foo3Factory(ModelFactory):
    __model__ = Foo3

class BarFactory(ModelFactory):
    __model__ = Bar

class BazFactory(ModelFactory):
    __model__ = Baz

# 生成仅含必填字段的实例(排除指定可选字段)
baz_required = BazFactory.build(exclude={"region", "bar__zipcode", "bar__foo__city"})

# 生成包含所有字段的实例
baz_full = BazFactory.build()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:10:12