Factory Boy:定义依赖于其他字段的字段

25

如何使用 factory-boy 定义依赖于其他字段的字段?

例如,我想定义一个依赖于 Userfirst namelast nameemail

我尝试使用 post_generation 装饰器。但是,我的系统要求在创建实例之前定义电子邮件。


也许您可以展示一下您当前的逻辑/代码,这样我们就能更好地了解您所拥有的,从而给出一个方向。 - davejal
2个回答

34

使用LazyAttribute

来自文档:

LazyAttribute是扩展工厂的一种简单但极其强大的构建组件。

它以要调用的方法(通常是lambda)作为参数;该方法应将正在构建的对象作为唯一参数接受,并返回一个值。

class UserFactory(factory.Factory):
    class Meta:
        model = User

    username = 'john'
    email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)

或者使用lazy_attribute修饰符。

来自文档:

如果简单的lambda表达式不够用,您可以使用lazy_attribute()装饰器。

这将修饰一个实例方法,该方法应接受一个self参数;方法的名称将用作填充返回值的属性的名称:

class UserFactory(factory.Factory)
    class Meta:
        model = User

    name = u"Jean"

    @factory.lazy_attribute
    def email(self):
        # Convert to plain ascii text
        clean_name = (unicodedata.normalize('NFKD', self.name)
                        .encode('ascii', 'ignore')
                        .decode('utf8'))
        return u'%s@example.com' % clean_name

2
您还可以使用factory.SelfAttribute来处理依赖于其他字段的字段。
在您的情况下,LazyAttribute效果很好,而且很清晰,但如果您需要做一些更复杂的操作,SelfAttribute将是更好的选择。
例如,假设我们有一个名为Course的实体,其中包含start_dateend_date。每个课程都有一个期末考试,必须在课程开始后参加,并在课程结束前完成。然后,model.py应该如下所示:
class Course(models.Model):
    start_date = models.DateTimeField(auto_now_add=False, blank=False)
    end_date = models.DateTimeField(auto_now_add=False, blank=False)

class Test(models.Model):
    course = models.ForeignKey(
        to=Course, blank=False, null=False, on_delete=models.CASCADE
    )
    date = models.DateField()

现在,让我们创建我们的factory.py文件:
class CourseFactory(DjangoModelFactory):
    class Meta:
        model = Course

    start_date = factory.Faker(
        "date_time_this_month", before_now=True, after_now=False, tzinfo=pytz.UTC
    )
    end_date = factory.Faker(
        "date_time_this_month", before_now=False, after_now=True, tzinfo=pytz.UTC
    )

class TestFactory(DjangoModelFactory):
    class Meta:
        model = Test

    date = factory.Faker(
        "date_between_dates",
        date_start=factory.SelfAttribute('..course.start_date'),
        date_end=factory.SelfAttribute('..course.end_date')
    )
    course = factory.SubFactory(CourseFactory)


正如您在TestFactory中所看到的,我们可以引用正在构建的对象的另一个字段或其属性。

date = factory.Faker( 应该改为 date = factory.Maybe( 吗? - thoroc
1
不完全是。我的意思是,对于这种特定的情况,我们有一些课程,它们有一个开始日期和结束日期,并且我们希望在这个日期范围内进行测试。所以,“date_between_dates”很适合。另一方面,“factory.Maybe”接受一个布尔类型的“decider”字段和两个声明。根据布尔值的不同,其中一个声明将被执行以生成值。 - mathias.lantean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接