[Fixed]-Django factory boy factory with OneToOne relationship and related field

14👍

I believe this is because you have a circular reference in your factory definitions. Try removing the line account = factory.RelatedFactory(AccountFactory) from the UserFactory definition. If you are always going to invoke the account creation through AccountFactory, then you shouldn’t need this line.

Also, you may consider attaching a sequence to the name field, so that if you ever do need more than one account, it’ll generate them automatically.

Change: username = "bob" to username = factory.Sequence(lambda n : "bob {}".format(n)) and your users will be named “bob 1”, “bob 2”, etc.

👤hgcrpd

2👍

To pass result of calling UserFactory to AccountFactory you should use factory_related_name (docs)

Code above works next way:

  • AccountFactory for instantiating needs SubFactory(UserFactory).
  • UserFactory instantiates User.
  • UserFactory after instantiating calls RelatedFactory(AccountFactory)
  • Recursion,.. that is broken due to unique username constraint (you probably want to generate usernames via FuzzyText or Sequence)

So you need write UserFactory like this:

class UserFactory(factory.django.DjangoModelFactory):
    account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
    username = factory.Sequence(lambda a: 'email%04d@somedomain.com' % a)
    # rest of code

But you can still experience issues with already written tests. Imagine you have in tests places like next:

user = UserFactory()
account = Account(user=user)

Then adding RelatedFactory will break tests. If you haven’t lots of tests and contributors in your project, you could rewrite them. But if not, it is not an option. Here is how it could be handled:

class UserFactory(factory.django.DjangoModelFactory):
    class Params:
        generate_account = factory.Trait(
            account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
        )

Then code above won’t be broken, because default call of UserFactory won’t instantiate AccountFactory. To instantiate user with account:

user_with_account = UserFactory(generate_account=True)

0👍

You can set account=None in your Subfactory, see the example here:
https://factoryboy.readthedocs.io/en/stable/recipes.html#example-django-s-profile

user = factory.SubFactory('app.factories.UserFactory', account=None)

Leave a comment