工厂男孩:定义依赖于其他领域的领域
factory boy: define field that depends on other field
如何使用 factory-boy
定义依赖于其他字段的字段?
例如,我想定义一个 email
,它依赖于 User
的 first name
和 last name
。
我尝试使用 post_generation
装饰器。但是,我的系统需要在创建实例之前定义电子邮件。
from the docs:
The LazyAttribute is a simple yet extremely powerful building brick for extending a Factory.
It takes as argument a method to call (usually a lambda); that method should accept the object being built as sole argument, and return a value.
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'john'
email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)
或 lazy_attribute 装饰器。
from the docs:
If a simple lambda isn’t enough, you may use the lazy_attribute() decorator instead.
This decorates an instance method that should take a single argument, self; the name of the method will be used as the name of the attribute to fill with the return value of the method:
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
您还可以对依赖于其他字段的字段使用 factory.SelfAttribute。
在你的情况下,LazyAttribute
工作正常,而且很清楚,但如果你需要做一些更复杂的事情,SelfAttribute
它会是一个更好的选择。
例如,假设我们有一个名为 Course 的实体,具有 start_date
和 end_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
中看到的,我们可以引用正在构造的对象的另一个字段或其属性。
如何使用 factory-boy
定义依赖于其他字段的字段?
例如,我想定义一个 email
,它依赖于 User
的 first name
和 last name
。
我尝试使用 post_generation
装饰器。但是,我的系统需要在创建实例之前定义电子邮件。
from the docs:
The LazyAttribute is a simple yet extremely powerful building brick for extending a Factory.
It takes as argument a method to call (usually a lambda); that method should accept the object being built as sole argument, and return a value.
class UserFactory(factory.Factory):
class Meta:
model = User
username = 'john'
email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)
或 lazy_attribute 装饰器。
from the docs:
If a simple lambda isn’t enough, you may use the lazy_attribute() decorator instead.
This decorates an instance method that should take a single argument, self; the name of the method will be used as the name of the attribute to fill with the return value of the method:
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
您还可以对依赖于其他字段的字段使用 factory.SelfAttribute。
在你的情况下,LazyAttribute
工作正常,而且很清楚,但如果你需要做一些更复杂的事情,SelfAttribute
它会是一个更好的选择。
例如,假设我们有一个名为 Course 的实体,具有 start_date
和 end_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
中看到的,我们可以引用正在构造的对象的另一个字段或其属性。