7

Let's say you have these Django models related this way:

class Service:
   restaurant = models.ForeignKey(Restaurant)
   hist_day_period = models.ForeignKey(DayPeriod)

class DayPeriod:
   restaurant = models.ForeignKey(Restaurant)

I want to create a Service object, using a Factory. It should create all 3 models but use the same restaurant.

Using this code:

class ServiceFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Service

    restaurant = factory.SubFactory('restaurants.factories.RestaurantFactory')

    hist_day_period = factory.SubFactory(
        'day_periods.factories.DayPeriodFactory', restaurant=restaurant)

Factory boy will create 2 different restaurants:

s1 = ServiceFactory.create()
s1.restaurant == s1.hist_day_period.restaurant
>>> False

Any idea on how to do this? It's unclear to me if I should use related factors instead of SubFactory to accomplish this.

0

3 Answers 3

14
+50

You want to use factoryboy's parents and SelfAttribute

class ServiceFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Service

    restaurant = factory.SubFactory('restaurants.factories.RestaurantFactory')

    hist_day_period = factory.SubFactory(
        'day_periods.factories.DayPeriodFactory',
        restaurant=factory.SelfAttribute('..restaurant')
    )

with this test app I get

In [1]: from service.tests import ServiceFactory
In [2]: s1 = ServiceFactory.create()
In [3]: s1.restaurant == s1.hist_day_period.restaurant
Out[3]: True

In [4]: s1.restaurant_id
Out[4]: 4

In [5]: s1.hist_day_period.restaurant_id
Out[5]: 4
Sign up to request clarification or add additional context in comments.

4 Comments

It's not clear how this solves the OP's problem of creating three Service records but only one Restaurant record.
OP states " It should create all 3 models, but use the same restaurant.". I read this as Service, Resturant and DayPeriod but only one Restaurant, the code sample he provides looks like it backs this up
Good answer, I think it also makes sense to mention lazyattribute and explain a reason to use or not to use RelatedFactory here.
Thanks a lot, exactly what I needed. You deserve this bounty :)
4

I've gone back and forth on whether it makes sense to create related or FK objects from within a factory, or separately. As you've already found, you don't necessarily want to get a new restaurant every time you call ServiceFactory(). I think you're better off keeping them simple, at the expense of having to use slightly more verbose calling code.

Comment out the restaurant = factory.SubFactory line, then call your factory like:

restaurant = Restaurant.objects.get(foo='bar')
ServiceFactory.create_batch(3, restaurant=restaurant)

Or, if you do want to use a factory to create the restaurant but you only want ONE restaurant created:

restaurant = RestaurantFactory()
ServiceFactory.create_batch(3, restaurant=restaurant)

IOTW, have your factories make as few assumptions as possible, so you have the flexibility to build up whatever data structures you need from tests or different parts of your system.

1 Comment

Thanks @shacker, your answer helped me to take some height, and I think you're right about the flexibility. However, the dinosaurwaltz answer is exactly the technical way to do what I wanted to do :)
0

Instead of using a SubFactory you can instead use an Iterator with a queryset.

For example:

class ServiceFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Service

    restaurant = factory.Iterator(models.Restaurant.object.all())

    hist_day_period = factory.SubFactory(
        'day_periods.factories.DayPeriodFactory', restaurant=restaurant)

I'm not sure about the best place to create that Restaurant, though. In our codebase I override DiscoverRunner and create models used for this purpose there (in setup_databases, which feels like a hack).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.