Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use lazy_attribute with Faker in Factory Boy

Context: I have a model with two dates, I want to use factory.Faker for both of them but the second date should always be greater that the first one.

I tried this:

Model excerpt:

class Event(models.Model):
     execution_start_date = models.DateTimeField()
     execution_end_date = models.DateTimeField()

Factory:

class EventFactory(factory.DjangoModelFactory):
    class Meta:
        model = Event
        strategy = factory.BUILD_STRATEGY

    execution_start_date = factory.Faker('date_time_this_year', tzinfo=pytz.utc)
    @factory.lazy_attribute
    def execution_end_date(self):
        return factory.Faker('date_time_between_dates',
                             datetime_start=self.execution_start_date,
                             datetime_end=now(),
                             tzinfo=pytz.utc)

But when I try to use the factory from the python shell I got this:

In [3]: e = EventFactory()

In [4]: e.execution_end_date
Out[4]: <factory.faker.Faker at 0x1103f51d0>

The only way I managed to make it work was with like this:

@factory.lazy_attribute
def execution_end_date(self):
    # return factory.Faker('date_time_between_dates',
    #                      datetime_start=self.execution_start_date,
    #                      datetime_end=now(),
    #                      tzinfo=pytz.utc)
    faker = factory.Faker._get_faker()
    return faker.date_time_between_dates(datetime_start=self.execution_start_date,
                                         datetime_end=now(),
                                         tzinfo=pytz.utc)

But I honestly think there is a better way to do it.

My dependencies are:

  • Django (1.8.18)
  • factory-boy (2.8.1)
  • Faker (0.7.17)
like image 823
diegueus9 Avatar asked Jul 12 '17 22:07

diegueus9


1 Answers

When lazy_attribute come into play you already have generated object on your hand. So you can work with, for example, random and timedelta, like this:

@factory.lazy_attribute
def execution_end_date(self):
    max_days = (now() - self.execution_start_date).days
    return self.execution_start_date + timedelta(random.randint(1, max_days))

or some other way to generate random date. There is no point to stick to factory_boy.Faker

EDIT

After my first answer I manage to found a way to do what you want, it's really simple.You just need to call generate() method with empty dict from Faker:

@factory.lazy_attribute
def execution_end_date(self):
    return factory.Faker('date_time_between_dates',
                         datetime_start=self.execution_start_date,
                         datetime_end=now(),
                         tzinfo=pytz.utc).generate({})
like image 148
Dmitriy Repin Avatar answered Sep 22 '22 07:09

Dmitriy Repin