Django get_or_create

Django get_or_create accomplishes a couple of things:

  1. It’s a convenient way to avoid boilerplate get and create code
  2. More importantly, it allows you to avoid duplicates when you have multiple processes trying to create objects

Here’s how you would usually deal with get/create:

try:
    author = Author.objects.get(name='Leo Tolstoy')
except DoesNotExist:
    author = Author(name='Leo Tolstoy')
    author.save()

# Do something with author

You try to grab the author from the database, if you can’t find him, you create him. If you have multiple requests executing this code, there can be a race condition. It’s possible that both threads will find that the author doesn’t exist and both will create that author, creating duplicates.

Django get_or_create on the other hand is atomic (assuming everything is configured correctly, more on that below), meaning that it’s a single operation and so can be used concurrently. Here’s how to use it:

author, created = Author.objects.get_or_create(name='Leo Tolstoy')
# Do something with author

Uniqueness Constraint

The way get_or_create works underneath is that it first tries to grab the object with a get(), if it can’t it will try to create the object inside of a transaction, if that fails with an IntegrityError, it will then grab the object and return it. That means that in a potential race condition, if both processes got past the first get() and are trying to create the object and there is no uniqueness constraint, they will both create the object.

That’s why to guarantee an atomic transaction, there has to be a uniqueness constraint on the fields you are querying for. For example, this will potentially create duplicates and isn’t safe:

class Author(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

author, created = Author.objects.get_or_create(first_name='Leo', last_name='Tolstoy')

This is safe:

class Author(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    class Meta:
        unique_together = ('first_name', 'last_name')

author, created = Author.objects.get_or_create(first_name='Leo', last_name='Tolstoy')

Django get_or_create Usage

Django get_or_create returns the object that it got and a boolean value that specifies whether the object was created or not. If get_or_create finds multiple objects it will raise a MultipleObjectsReturned exception.

Defaults

You can pass a defaults argument to get_or_create that will only be used in the create portion of the query:

author, created = Author.objects.get_or_create(name='Leo Tolstoy', defaults={'books_in_store': 0, 'store': 'Amazon'})

To use the actual field defaults, if you have one, you can use defaults__exact to not be confused with the get_or_create defaults.

Complex Queries

You can have more complex queries using filters and Q objects

author, created = Author.objects.filter(
    Q(name='John Smith') | Q(name='Leo Tolstoy')
).get_or_create(name='Leo Tolstoy')

This query will try to find either ‘John Smith’ or ‘Leo Tolstoy’, if it doesn’t find either it will create ‘Leo Tolstoy’.

Many to Many

You can use Django get_or_create with ManyToMany fields like so:

class Author(models.Model):
    name = models.CharField(max_length=255, unique=True)

class Book(models.Model):
    authors = models.ManyToManyField(Author)
    title = models.CharField(max_length=255)

book = Book.objects.get(title='War and Peace')
author, created = book.authors.get_or_create(name='Leo Tolstoy')

This will get or create author ‘Leo Tolstoy’ for the book ‘War and Peace’.

Leave a Reply