How to use factory.LazyAttribute with Faker() functions
Asked Answered
V

2

5

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?

Vanesavanessa answered 6/7, 2020 at 12:26 Comment(0)
S
9

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)
Supersonics answered 6/7, 2020 at 12:46 Comment(4)
Thanks, given factory.fuzzy.FuzzyChoice is depreciated, how would you implement this with Faker so that it is called on each object?Vanesavanessa
You can also use factory.Faker('random_choice', elements=["My Title", None]). And I'll have to look into this FuzzyChoice deprecation, it has features that Faker doesn't…Supersonics
How does that work since description is a class member variable. When we modify the value of a class variable, we change the value across all of the instances at the same time, right? ThanksLes
In other words, if I call MyFactory to create two instances, will the two instances have the same value for the class member variable description?Les
V
5

Or if you need a more specific interaction with the Faker library:

from faker import Faker as RealFaker
real_faker = RealFaker()

Inside the factory:

factory.LazyAttribute(lambda a: real_faker.email())
Verisimilar answered 9/8, 2021 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.