我正在尝试创建一些模拟数据,其中我的一个类(我通过继承
Pydantic.BaseModel
为其指定了 Pydantic 数据类)具有日期属性。我想确保 end_date
始终晚于 start_date
,并使用 pydantic 验证器装饰器验证这一点:
from pydantic import BaseModel, validator
from datetime import datetime
class Dates(BaseModel):
start_date: datetime
end_date: datetime
@validator('end_date')
def ensure_end_date_is_after_start_date(cls, end_date, values):
if not end_date > values['start_date']:
raise ValueError(f"End date {end_date} is not after start date {values['start_date']}.")
return end_date
假设我现在想使用
pydantic_factories
包创建一些模拟数据。我定义了一个继承自 ModelFactory
的类,以便稍后创建一些模拟数据。
from pydantic_factories import ModelFactory
ModelFactory.seed_random(0)
class DatesFactory(ModelFactory):
__model__ = Dates
但是,以这种方式,我不能保证
start_date
会在end_date
之前。我设置了 ModelFactory.seed_random(0)
,以获得可重复的结果。事实上,当我创建模拟数据时,我发现日期验证失败:
my_dates = DatesFactory()
my_dates.build()
Traceback (most recent call last):
.
.
.
pydantic.error_wrappers.ValidationError: 1 validation error for Dates
end_date
End date 2006-06-21 01:20:01 is not after start date 2022-02-03 12:04:21. (type=value_error)
我的问题:如何确保获得符合我的验证标准的模拟数据?以可扩展到更复杂的验证的方式来解决这个问题的常用方法是什么?
提前致谢!
编辑:当前的示例可能有点愚蠢;我知道可以通过以不同方式定义我的属性来规避它(例如仅定义
start_date
并始终为正 duration
)。然而,我想将这个例子扩展到更复杂的东西。
完整的 MWE:
from pydantic import BaseModel, validator
from datetime import datetime
from pydantic_factories import ModelFactory
class Dates(BaseModel):
start_date: datetime
end_date: datetime
@validator('end_date')
def ensure_end_date_is_after_start_date(cls, end_date, values):
if not end_date > values['start_date']:
raise ValueError(f"End date {end_date} is not after start date {values['start_date']}.")
return end_date
class DatesFactory(ModelFactory):
__model__ = Dates
ModelFactory.seed_random(0)
my_dates = DatesFactory()
my_dates.build()
如文档此处中所述,由 将
factory_use_construct
设置为 true .build()
方法将忽略运行验证。
factory_use_construct – 一个布尔值,确定实例化模型时是否进行验证。仅 pydantic 模型支持此功能。
from pydantic import BaseModel, validator
from datetime import datetime, timedelta
from pydantic_factories import ModelFactory
class Dates(BaseModel):
start_date: datetime
end_date: datetime
@validator('end_date')
def ensure_end_date_is_after_start_date(cls, end_date, values):
if not end_date > values['start_date']:
raise ValueError(f"End date {end_date} is not after start date {values['start_date']}.")
return end_date
class DatesFactory(ModelFactory[Dates]):
__model__ = Dates
factory_result = DatesFactory(factory_use_construct=True)
print(factory.dict())
>>> {'start_date': datetime.datetime(2007, 12, 24, 3, 16, 28), 'end_date': datetime.datetime(1994, 11, 1, 14, 50, 48)}
或者通过指定
start_date
和 end_date
, 的默认值
kwargs – 任何 kwargs。如果 field_meta 名称在 kwargs 中设置,则将使用它们的值。
start_date = datetime.now()
end_date = start_date + timedelta(days=5)
fields_meta = {"start_date": start_date, "end_date": end_date}
factory_result = DatesFactory.build(**fields_meta)
# To make sure your data is valid
factory_result.validate(factory_result.dict())
注意:
pydantic-factories 1.17.3
的下一个版本发布为 polyfactory
我将是第一个承认这并不完全令人满意的人,但在像这样的复杂情况下,我们已经采取了在必填字段上自定义生成器方法来强制执行合规性规则。对于你的情况,你可以这样做:
from datetime import datetime, timedelta
from polyfactory.factories.pydantic_factory import ModelFactory
from polyfactory.decorators import post_generated
import random
class DatesFactory(ModelFactory):
__model__ = Dates
@post_generated
@classmethod
def end_date(cls, start_date: datetime) -> datetime:
""" """
return start_date + timedelta(days=random.randint(1, 1000))
post_generated
注释使我们可以访问基本模型上定义的start_date
等字段。从那里,您将能够定义您的用例所需的任何自定义逻辑。
您也可以在没有注释的情况下覆盖每个字段的实现,但无法访问其他工厂字段。