簡體   English   中英

Django:通過自動將計數器附加到title / slug來保存唯一條目

[英]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方法中執行兩次寫操作:

  1. 使用臨時隨機slug保存實例(以滿足唯一性約束)
  2. 使用PK計算slu and並再次保存

像這樣的東西:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM