Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Django get_or_create cause this IntegrityError?

I have a model that has this field on it:
token = models.CharField(max_length=32, default="", unique=True, null=False, db_index=True)

In the save() method I set this field to a 32 character random string using this method:

def save(self, *args, **kwargs):
    if (self.token is None or len(self.token) == 0):
        self.token = random_identifier()
    super(SessionPassthrough, self).save(*args, **kwargs)

def random_identifier(n=32):
    """ Generate a random identifier of length n. 

    From http://stackoverflow.com/questions/2257441/python-random-string-generation-with-upper-case-letters-and-digits"""
    return ''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(n))

However I am getting this error whenever I try to create a new instance of the model:
IntegrityError: duplicate key value violates unique constraint "wakelytics_sessionpassthrough_token_key"

To create the instance I call this method:

@staticmethod
def for_session(session):
    sp, c = SessionPassthrough.objects.get_or_create(session=session)
    return sp

Does get_or_create() call the method's save() function before writing to the database? Answer: Yes

I get an IntegrityError whenever I call the method for the first time with a session, and continue getting the error for a few minutes. It then returns correctly. What is causing this?

like image 667
Mantas Vidutis Avatar asked Jun 16 '11 01:06

Mantas Vidutis


People also ask

What causes IntegrityError in Django?

After some digging, reading, and thinking, I realised that the cause of the IntegrityError isn't Python—it's Postgres. Handling the error in Python is suppressing an error about a failed transaction, but the transaction is still failed.

Is get_ or_ create atomic?

get_or_create is described as being atomic in the documentation, but that is not the case. Since it first tries to retrieve an existing record and if that fails it tries to create a new one, another thread can create that record in the small interval between the two operations.


1 Answers

Yes.

https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create

From the docs:

The new object will be created roughly according to this algorithm:

defaults = kwargs.pop('defaults', {})
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
obj = self.model(**params)
obj.save()
like image 134
rolling stone Avatar answered Nov 15 '22 04:11

rolling stone