Using `django_get_or_create` with onetoone related field

50 views Asked by At

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
1

There are 1 answers

0
Rémi Desgrange On BEST ANSWER

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