Kurt Peek
Kurt Peek

Reputation: 57541

In Django Factory Boy, is it possible to specify custom FactoryOptions for a certain instance?

I'm writing unit tests for a Django app which use factory_boy test fixtures. In some cases, one fixture might try to create an object that was already created by another fixture, which leads to error messages similar to the following:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "lucy_web_sessiontype_title_c207e4f8_uniq"
DETAIL:  Key (title)=(Postpartum Doula Support) already exists.

To avoid this, my first inclination is to write a try..except block, like so:

try:
    SessionTypeFactory(title='Welcome')
except psycopg2.IntegrityError as e:
    pass

However, it seems like it would be more elegant to use the django_get_or_create option described at http://factoryboy.readthedocs.io/en/latest/orms.html#factory.django.DjangoOptions.django_get_or_create.

However, as far as I can tell from http://factoryboy.readthedocs.io/en/latest/reference.html#factory.FactoryOptions, options specifie in the factory's Meta class will apply to all instances, whereas I would only like it to only apply to this instance. Is it possible to specify these options in the constructor?

Upvotes: 1

Views: 2658

Answers (2)

Xelnor
Xelnor

Reputation: 3589

The doc is slightly misleading. What is documented in the FactoryOptions section is the list of options available in the class Meta part.

In your case, you could go with the following code sample:

class SessionTypeFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.SessionType
        django_get_or_create = ['title']

    # your fields here

This will perform a SessionType.objects.get_or_create(title=fields.pop('title'), defaults=fields) every time you create an instance. Since you have a unique condition on the title field, you can safely put that behavior in place at the SessionTypeFactory level.

Upvotes: 3

Kurt Peek
Kurt Peek

Reputation: 57541

After perusing the source code a bit, it seems from this line in the DjangoModelFactory class definition that this is not possible. The class contains the following class method:

class DjangoModelFactory(base.Factory):

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Create an instance of the model, and save it to the database."""
        manager = cls._get_manager(model_class)

        if cls._meta.django_get_or_create:
            return cls._get_or_create(model_class, *args, **kwargs)

        return manager.create(*args, **kwargs)

From this, it seems that django_get_or_create is a class attribute and not an instance attribute, so it is not possible to specify django_get_or_create per instance. Please correct me if I'm wrong!

Upvotes: 1

Related Questions