![](/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.