factory boy: define field that depends on other field
Use LazyAttribute:
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)
or the lazy_attribute decorator.
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
You could also use factory.SelfAttribute for fields that depends on others fields.
In your case, LazyAttribute
works fine, and it's quite clear, but if you need to do something a little more complex, SelfAttribute
it's gonna be a better option.
For example, let's say we have an entity called Course with a start_date
and end_date
. Each course has a final test which must be attended after the course starts and before the course ends. Then, model.py should look like this:
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()
Now, let's create our 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)
As you can see in TestFactory
we can reference another field of the object being constructed, or an attribute thereof.