django - 检测IntegrityError而不使用“save()”

时间:2014-12-26 18:28:17

标签: django

好的,我需要一点帮助。

我有一个名为slug = models.SlugField(unique=True)的字段的模型,如果slug已经存在,我试图通过将save()附加到slug来设置1上的此字段,依此类推。

我想考虑比赛条件。

def set_uniqslug(self, slug, i=0):
    new_slug = u"{}{}".format(slug, str(i) if i else '')
    try:
        with transaction.atomic():
            self.slug = slugify(new_slug.lower())
            self.save()
            return self
        return self
    except IntegrityError as e:
        i += 1
        return set_uniqslug(self, slug, i)

def save(self, *args, **kwargs):
    if not self.pk:
        set_uniqslug(self.name.lower()) # <--- but it does "save" above. 
        # i want something like:
        # self.slug = self.get_uniqslug(self.name.lower())
        super(Company, self).save(*args, **kwargs)

我的问题是,如果我调用set_uniqslug(),它需要尝试保存,只是为了知道是否存在IntegrityError。在我的代码中,它进入无限循环。

如果存在IntegrityError,我怎么知道如果不保存,然后将唯一的slug返回到save()方法?

更新

我试过这个:

with transaction.atomic():
    if Company.objects.filter(slug=new_slug).exists():
        i += 1
        return self.set_uniqslug(slug, i)
    return new_slug

它正在运作,但是通过锁定READ - 动作我有胃痛。我这样做是不是阻止了其他查询或做任何其他不好的事情?

1 个答案:

答案 0 :(得分:1)

您的检查和设置版本可能无效。这将取决于您的数据库及其事务隔离级别的实现;但以PostgreSQL为例,默认的READ COMMITTED隔离级别不会阻止另一个事务在您的检查和设置之间插入具有相同slug的行。

因此,请使用您原始的,乐观的锁定理念。正如Hugo Rodger-Brown指出的那样,你可以通过调用超类的save()来避免无限循环。

最后,您可能想要考虑另一种slug格式。很多时候slug将包含数据库id(实际上类似于StackOverflow本身),这消除了重复slu的可能性。