pythondjangofactory-boy

Using `django_get_or_create` with onetoone related field


Given this django model

from django.db import Model
from django.contrib.auth.models import User
class Customer(models.Model):
    user = models.OneToOneField(User, on_delete=models.PROTECT)
    some_other_field = model.CharField(...)

I have created 2 factory for the user and the customer model:

import factory
class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User
        django_get_or_create = ('username',)

    first_name = factory.Faker("first_name", locale="fr_FR")
    last_name = factory.Faker("last_name", locale="fr_FR")
    username = factory.LazyAttribute(lambda m: f"{m.first_name[0]}{m.last_name[0]}".lower())
    email = factory.LazyAttribute(lambda m: f"{m.first_name.lower()}.{m.last_name.lower()}@ielo.net")
    customer = factory.RelatedFactory(CustomerFactory, factory_related_name="user", user=None)
    is_staff = False


class CustomerFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = "customers.Customer"
    user = factory.SubFactory('myapp.tests.fixtures.UserFactory', customer=None)

To avoid flaky tests, I have set the django_get_or_create, since most of the time I just want a user, and I create specific classes for specific cases (UserIsStaffFactory, UserSuperAdminFactory)

I copied the RelatedFactory/SubFactory from https://factoryboy.readthedocs.io/en/stable/recipes.html#example-django-s-profile but If I run:

u1 = UserFactory(username='foo')
u2 = UserFactory(username='foo')
# raise IntegrityError, UNIQUE constraint failed: customers_customer.user_i

Solution

  • I have solved the problem like this:

    import factory
    class UserFactory(factory.django.DjangoModelFactory):
        first_name = factory.Faker("first_name", locale="fr_FR")
        last_name = factory.Faker("last_name", locale="fr_FR")
        username = factory.LazyAttribute(lambda m: f"{m.first_name[0]}{m.last_name[0]}".lower())
        email = factory.LazyAttribute(lambda m: f"{m.first_name.lower()}.{m.last_name.lower()}@fakecompany.com")
        customer = factory.RelatedFactory(CustomerFactory, factory_related_name="user", user=None)
        is_staff = False
    
        class Meta:
            model = User
            django_get_or_create = ('username',)
    
    class CustomerFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = "customers.Customer"
            django_get_or_create = ("user",)
        user = factory.SubFactory('private.tests.fixtures.UserFactory')
    

    which seems pretty straightforward 😅