我有一个名为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
- 动作我有胃痛。我这样做是不是阻止了其他查询或做任何其他不好的事情?
答案 0 :(得分:1)
您的检查和设置版本可能无效。这将取决于您的数据库及其事务隔离级别的实现;但以PostgreSQL为例,默认的READ COMMITTED
隔离级别不会阻止另一个事务在您的检查和设置之间插入具有相同slug的行。
因此,请使用您原始的,乐观的锁定理念。正如Hugo Rodger-Brown指出的那样,你可以通过调用超类的save()
来避免无限循环。
最后,您可能想要考虑另一种slug格式。很多时候slug将包含数据库id(实际上类似于StackOverflow本身),这消除了重复slu的可能性。