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?
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.
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.
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With