Assume a django model with two fields: - created - modified Each field are integers, unique and ever-increasing in value. An object should have the same value for created and modified when saved the first time, and the modified field should be increated to the next free value larger than itself on each save.
I've created the following fields for the purpose:
class CreatedVerisonField(models.BigIntegerField):
update_on_each_save = False
def __init__(self, *args, **kwargs):
kwargs.setdefault('editable', False)
kwargs.setdefault('blank', True)
kwargs.setdefault('unique', True)
models.BigIntegerField.__init__(self, *args, **kwargs)
def pre_save(self, model, add):
if add or self.update_on_each_save:
value = self.get_next_value(model)
setattr(model, self.attname, value)
return value
else:
return super(CreatedVerisonField, self).pre_save(model, add)
def get_next_value(self, model):
objs = model.__class__.objects.all()
fields = self._get_fields(model)
if objs:
# new version is max of all version fields + 1
value = max(objs.aggregate(*map(lambda x: Max(x), fields)).values()) + 1
else:开发者_C百科
value = 1
return value
def _get_fields(self, model):
fields = []
for f in model._meta.fields:
if isinstance(f, CreatedVerisonField):
fields.append(f.db_column if f.db_column else f.name)
return fields
class ModifiedVersionField(CreatedVerisonField):
def __init__(self, *args, **kwargs):
self.update_on_each_save = True
CreatedVerisonField.__init__(self, args, kwargs)
A simple test class using these fields:
class TestModel(models.Model):
created = CreatedVerisonField()
modified = ModifiedVersionField()
When creating and saving "TestModel" objects from multiple threads, I get IntegretyError due to the unique-constraint is violated. Looking add the pre_save code, it's quite obvious that this race exists. How do I fix this?
The only way to do thread-safe database access is to lock the tables before access. A cursory search online turned up this snippet: http://djangosnippets.org/snippets/2039/. Maybe you can do something with that. The Django documentation's section on transactions will be useful to you as well: https://docs.djangoproject.com/en/dev/topics/db/transactions/
精彩评论