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

