![](/img/trans.png)
[英]Slugify not saving title as slug Django 2: NoReverseMatch error
[英]Django: saving unique entries by automatically appending a counter to the title/slug
我有一个Django表单条目,我希望保存到数据库的模型具有唯一的标题,或者更确切地说是一个独特的“slug”(从标题派生)。 我通过在slu子上附加一个计数器来做到这一点。 所以像“这是一个冠军1”,可能后来“这是一个冠军2”。
但是如果两个用户同时提交具有相同标题的条目,则只有一个条目将进入数据库(slug字段是唯一的); 保存另一个将抛出IntegrityError。 为了创建一个独特的计数器,我现在循环遇到这样一个重复的条目,如下所示:
class MyClass(models.Model):
self.title = models.CharField()
self.slug = models.SlugField(unique=True, blank=False)
def save(*args, **kwargs):
baseslug = slugify(self.title)
self.slug = baseslug + "-1"
count = 1
while count < MAXCOUNT: # MAXCOUNT something like 1000
try:
super(MyClass, self).save(*args, **kwargs)
except IntegrityError as exc:
count += 1
self.slug = baseslug + "-{:d}".format(count)
else:
break # we're fine; break out of the loop
我通过管理员执行此操作,其中未明确输入slug(但是在save()
方法中创建)。
我的问题是 :这是一个好主意吗? 不知何故,感觉就像一个解决方案有更好的解决方案。
我能想到的另一个解决方案是向不幸的第二个用户弹出一条消息,但这只会说“此时无法保存您的条目。请在几秒钟后重试。” 然后我还要首先从现有的(相同的除了计数器)slug中获取当前count
。 上述方法自动避免了这种情况,尽管如果有超过MAXCOUNT
相同的段MAXCOUNT
,它会遇到问题。
有没有其他建议,或者这似乎是一个可行的解决方案?
(注意:在输入我的问题时,我遇到了SO提出的类似问题 ,但是在保存条目之前首先执行get()
。这可能允许get()
和save()
之间有足够的时间来尝试保存重复的条目,从而导致IntegrityError。否则它似乎提出了与我现在几乎相同的解决方案。)
如果你只需要一个值来传递查询字符串,你可以只为模型添加一个属性,它结合了slug + pk:
class MyClass(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=300)
@property
def url_slug(self):
return '{}-{}'.format(self.slug, self.id)
然后你不必担心计数关闭,竞争条件等,你只有一个写操作。
您可以在save方法中执行两次写操作:
像这样的东西:
class MyClass(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(max_length=300, blank=True)
def save(self, *args, **kwargs):
if not self.slug:
# Slug field must be unique, so give it a temporary throw-away value
temp_slug = uuid.uuid4().hex
self.slug = temp_slug
super(MyClass, self).save(*args, **kwargs)
self.slug = "{}-{}".format(slugify(self.title), self.pk)
super(MyClass, self).save(*args, **kwargs)
这样做的优点是每次保存调用最多只需要两次DB写入。
如果你必须在slug内有一个计数器,那么更好的方法是:
class MyClass(models.Model):
self.title = models.CharField()
self.slug = models.SlugField(unique=True, blank=False)
def save(self, *args, **kwargs):
baseslug = slugify(self.title)
try:
last = MyClass.objects.filter(slug__regex="%s-\d+" % baseslug).latest('id')
# safe, becouse slugify won't return regex special chars
# also field with latest id will have highest counter in slug if it was populated that way
trash, count = last.slug.rsplit('-', 1)
count = int(count)+1
except:
count = 1
while count < MAXCOUNT:
self.slug = "%s-%d" % (baseslug, count)
try:
super(MyClass, self).save(*args, **kwargs)
break
except IntegrityError as exc:
count += 1
它只会查询数据库一次,以检查slug末尾的数字。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.