开发者

Avoiding IntegrityError in custon Django field

开发者 https://www.devze.com 2023-03-10 00:16 出处:网络
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 th

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/

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号