Search code examples
pythondjangofakerfactory-boy

How to use factory.LazyAttribute with Faker() functions


I am using factory_boy to build some fixtures in Django.

I want to use factory.LazyAttribute to build one attribute based on the condition of another attribute.

class MyFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = MyModel

    title = 'My Title' if random() < 0.5 else None
    description = factory.LazyAttribute(
        lambda a: factory.Faker(
            'paragraph', nb_sentences=1, variable_nb_sentences=False
        ) if a.title else None)

However, this returns a string being <factory.faker.Faker object at 0x000001B10597BB20> rather than executing the correct paragraph generation.

Where am I going wrong?


Solution

  • factory.Faker is a special object: when a factory instantiates an object, it will ask the factory.Faker proxy to get a random value from faker.

    The simplest way to achieve what you're looking for is to use factory.Maybe:

    class MyFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = MyModel
    
        title = factory.fuzzy.FuzzyChoice(["My Title", None])
        description = factory.Maybe('title', factory.Faker('paragraph'))
    

    Note that, in the code you shared, the title = "My title" if random() < 0.5 else None is computed exactly once, when Python parses the file. I've used factory.fuzzy.FuzzyChoice to have that random computation performed for each object. This function also uses factory_boy's randomness management features.

    Another option would be to use parameters (class Params):

    class MyFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = MyModel
    
        class Params:
            # Items here will be available to further declarations, but won't be
            # passed to the model constructor.
            description_contents = factory.Faker('paragraph')
    
        title = factory.fuzzy.FuzzyChoice(["My Title", None])
        description = factory.LazyAttribute(lambda a: a.description_contents if a.title else None)