Django模型字段设置默认值的更好方式

3

我正在实现一个 Django 模型中的字段,该字段调用以下函数作为默认值:

def get_default_value():
    a = MyModel.objects.aggregate(max_id=Max('id'))
    return get_unique_value_based_on_num(a['max_id'] or 0)

class MyModel:
    default_value_field = CharField(default=get_default_value)

虽然我可能错了,但我担心这种实现可能会导致竞态条件。

有没有更好的方法来做这件事?也许使用F对象或其他什么东西?

2个回答

1
为了避免竞争条件,最好让数据库处理表的完整性,这也是数据库的一项功能。
为此,需要捕获通过保存模型实例引发的任何IntegrityError异常,并在失败时尝试使用不同的值再次保存。
from django.db import IntegrityError, models, transaction


def get_default_value():
    a = MyModel.objects.aggregate(max_id=Max('id'))
    return get_unique_value_based_on_num(a['max_id'] or 0)

class MyModel(models.Model):
    # Have unicity enforced at database level with unique=True.
    default_value_field = models.CharField(max_length=200, unique=True)

    def save(self):
        if not self.default_value_field:
            max_tries = 100  # Choose a sensible value!
            for i in range(max_tries):
                try:
                    self.default_value_field = get_default_value()

                    # Atomic block to rollback transaction in case of IntegrityError.
                    with transaction.atomic():
                        super(MyModel, self).save()
                        break
                except IntegrityError:
                    # default_value_field is not unique, try again with a new value.
                    continue
            else:
                # Max tries reached, raise.
                raise IntegrityError('Could not save model because etc...')
        else:
            super(MyModel, self).save(*args, **kwargs)

我目前正在尝试使用 select_for_update 来查看它是否可行。我想这也是一个有效的替代方法,所以接受它。在循环内的 save 后面不应该有 break 吗? - sagarchalise
@sagarchalise 我之前不知道 select_for_update,但它似乎正是你所需要的。如果你成功使用它解决了问题,应该发布一个答案并接受它。 - aumo

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接