Is there a way to use a temporary non-field variable in factoryboy?

Question:

I am defining some factories for testing my e-commerce store. I have created a Faker provider that can return, for instance, a dictionary containing all the data for a random product.

I want to then use that dictionary to populate the fields in my factory. This is because the data in the dictionary is coherent. I don’t want to create a factory with data that is not coherent e.g. a Product with name: "Short sleeve t-shirt" and then description: "Beautiful, comfortable shoes." Is there a way that I can implement something similar to

class ProductFactory(factory.django.DjangoModelFactory):
    temporary_product_dict = fake.product_dict_from_provider()
    
    name = temporary_product_dict["name"]
    description = temporary_product_dict["description"]
    category = temporary_product_dict["category"]
    ...

    class Meta:
        model = models.Product

When I do this exact thing, I get an error which tells me that temporary_product_dict is not an attribute of the Product model.

Asked By: Tommy Wolfheart

||

Answers:

I have used class Params as follows:

class OptionTypeFactory(factory.django.DjangoModelFactory):
    """OptionType model factory."""

    class Meta:
        model = OptionType

    class Params:
        # Obtain coherent data for an option type from provider
        option_type = fake.product_option_type()

    name = Params.option_type["name"]
    display_name = Params.option_type["display_name"]
    index = Params.option_type["index"]
    created = datetime.datetime.strptime(
        Params.option_type["created"], "%Y-%m-%d %H:%M:%S"
    )
    updated = datetime.datetime.strptime(
        Params.option_type["updated"], "%Y-%m-%d %H:%M:%S"
    )


register(OptionTypeFactory)
Answered By: Tommy Wolfheart

I like the Params approach. Before learning about that approach, I used lazy attributes, where they can refer to previously declared and initialized attributes.

def get_random_city():
    # left as exercise
    pass


def get_city(zip_code: str):
    # left as exercise
    pass


class CityFactory():

    @factory.lazy_attribute
    def zip_code(self):
        while True:
            loc = get_random_city()
            if loc.postal_code and loc.place_name and loc.admin1_code:
                return loc.postal_code

    @factory.lazy_attribute
    def city(self):
        loc = get_city(self.zip_code)
        return loc.place_name

    @factory.lazy_attribute
    def state(self):
        loc = get_city(self.zip_code)
        return loc.admin1_code

Answered By: Darren Weber