factory boy: define field that depends on other field factory boy: define field that depends on other field django django

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.