Django: Using get_or_create to prevent race conditions

Adrienne Domingus
3 min readFeb 2, 2018

get_or_create, is an awesome helper utility to have at your disposal when you need an object matching some specifications, but there should only be exactly one match — you want to retrieve it if it already exists, and create it if it doesn’t.

However, there’s a scenario where it doesn’t quite do what we expect in the case of race conditions (exactly the thing we’re trying to prevent). There is a nod to what we’re about to talk about in the docs:

This method is atomic assuming correct usage, correct database configuration, and correct behavior of the underlying database. However, if uniqueness is not enforced at the database level for the kwargs used in a get_or_create call (see unique orunique_together), this method is prone to a race-condition which can result in multiple rows with the same parameters being inserted simultaneously.

Let’s talk about this in more detail. Here’s the relevant bit of Dango’s implementation of get_or_create:

lookup, params = self._extract_model_params(defaults, **kwargs)
try:
return self.get(**lookup), False
except self.model.DoesNotExist:
return self._create_object_from_params(lookup, params)

This does exactly what the name implies! It attempts to do a lookup based on the filter args that are passed in…

--

--