FactoryBoy assign attribute to attribute of SubFactory
Asked Answered
P

3

9

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?

Pod answered 31/7, 2013 at 18:55 Comment(2)
Is there a reason why you can't just create the related model, and pass that object in to MyModelFactory.create()?Byrnie
@BrianDant Yes you could definitely do that. I was just looking for a way of doing things all in one go and being as DRY as possible.Pod
W
10

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.

Woeful answered 6/7, 2018 at 10:23 Comment(0)
G
6

Use SelfAttribute:

class MyModelFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = MyModel
    dummy_created_by = factory.SubFactory(AdminUserFactory)
    created_by = factory.SelfAttribute('dummy_created_by.id')
Grunt answered 16/6, 2014 at 23:18 Comment(3)
What if I need to use SelfAttribute to assign the result of a method call, not an attribute?Rhatany
Is it possible to get a child object of dummy_created_by? For example if AdminUserFactory creates an AdminUser with some UserRight child rows, can I get the UserRight list or IDs? Something like created_by = factory.SelfAttribute('dummy_created_by__user_right.id')? (I realize I can factory.create the UserRights, I'm just trying to keep code neatly encapsulated.)Brycebryn
@Rhatany did you find an answer to your question? ThanksNatica
H
-2

Try to use Mixer:

from mixer.backend.django import mixer

mixer.blend(MyModel, created_by=lambda: mixer.blend(AdminUser).id)
Harbard answered 7/1, 2014 at 19:19 Comment(1)
Thanks @klen, I will have to check out mixer. But on this project we are committed to FactoryBoy, so am looking for a FactoryBoy resolution.Pod

© 2022 - 2024 — McMap. All rights reserved.