Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FactoryBoy assign attribute to attribute of SubFactory

In defining a ModelFactory in FactoryBoy, I need to access an attribute of another model created by SubFactory and assign it to this ModelFactory's attribute.

This is what I want to do:

import factory
class MyModelFactory(factory.DjangoModelFactory):
    FACTORY_FOR = MyModel

    created_by = factory.SubFactory(AdminUserFactory)**.id**

Obviously that doesn't work because there is no AdminUser object to access the id in the MyModelFactory class definition.

This is what I have done, but it is ugly:

import factory
class MyModelFactory(factory.DjangoModelFactory):
    FACTORY_FOR = MyModel

    dummy_created_by = factory.SubFactory(AdminUserFactory)
    created_by = factory.LazyAttribute(lambda o: o.dummy_created_by.id)

    @classmethod
    def _create(cls, target_class, *args, **kwargs):
        del kwargs['dummy_created_by']
        return super(MyModelFactory, cls)._created(
            target_class, *args, **kwargs)

I was trying to read through the Factory_Boy docs but didn't see a class or function that would allow me to lazily access the attribute. Does Anyone have any suggestions?

like image 270
emispowder Avatar asked Jul 31 '13 18:07

emispowder


People also ask

What is DjangoModelFactory?

DjangoModelFactory is a basic interface from factory_boy that gives "ORM powers" to your factories. It's main feature here is that it provides you with a common "create" and "build" strategies that you can use to generate objects in your tests.

What is Factoryboy?

factory_boy is a fixtures replacement based on thoughtbot's factory_bot. As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures with easy-to-use factories for complex objects.


2 Answers

From factoryboy 2.4.0 onwards you can use exclude. This lets you add a parameter to the factory that won't be passed onto the model class

import factory
class MyModelFactory(factory.DjangoModelFactory):
    created_by = factory.SubFactory(AdminUserFactory)
    created_by_id = factory.LazyAttribute(lambda o: o.created_by.id)

    class Meta:
        model = MyModel
        exclude = ['created_by']

I've shifted the OPs example to use created_by_id but you can rename it to make sense for you.

like image 153
dinosaurwaltz Avatar answered Sep 25 '22 10:09

dinosaurwaltz


Use SelfAttribute:

class MyModelFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = MyModel
    dummy_created_by = factory.SubFactory(AdminUserFactory)
    created_by = factory.SelfAttribute('dummy_created_by.id')
like image 37
dustinfarris Avatar answered Sep 22 '22 10:09

dustinfarris