Django根据id生成唯一的Slug

5
class Product(models.Model):
    title = models.CharField(max_length=75)

class Deal(models.Model):
    product = models.ForeignKey(Product)
    slug = models.SlugField(max_length=255, unique=True)

基本设置与上述类似,我想要为每个交易实例生成唯一的slug,使用其交易产品的标题和交易本身的id。例如:"apple-iphone-4s-161",其中161是交易的id,前面的文本是产品的标题。

为此,我该如何重写Deal模型的save()方法来应用它?

4个回答

8
当然,您可以简单地覆盖模型上的save()方法(或为post_save信号创建接收器)。它将类似于:
from django.template.defaultfilters import slugify

class Deal(models.Model):
product = models.ForeignKey(Product)
slug = models.SlugField(max_length=255, unique=True)

    def save(self, *args, **kwargs):
        super(Deal, self).save(*args, **kwargs)
        if not self.slug:
            self.slug = slugify(self.product.title) + "-" + str(self.id)
            self.save()

但是这个解决方案中不美观的地方在于,它会两次访问数据库(保存两次)。这是因为当创建新的Deal对象时,它在第一次保存之前没有id(你无法做太多改变)。


1
它不丑陋,而是唯一的解决方法。 - devmiles.com
1
它很丑陋 - 因为这种“slug”的想法本身并不好。我知道这样做更容易并且可以忘记它,但实际上这种“slug”只是数据库的非规范化。我认为最好通过其ID引用对象,并在视图和URL中伪造使用“slug”。 - jasisz
这不是很正确,你做了两次保存的无意义操作,super调用应该放在底部。 - Jota
@Jota 我认为你需要调用 Super 方法以获取一个 id。此外,请注意 SlugField 的默认长度为 50,因此在这种情况下,您可能需要截断标题,例如 self.slug = slugify(self.product.title[45]) + "-" + str(self.id) - its30

3

我遇到了这个问题,并测试了jasisz的解决方案,但是得到了“最大递归深度超限”的错误。因此,我稍作调整,以下是我的解决方案:

def save(self, *args, **kwargs):
    if not self.id:
        self.slug = slugify(self.title)
    super(Node, self).save(*args, **kwargs)

你可以根据需要编辑此内容,它会检查此记录是否存在,如果不存在,则创建slug字段,否则将其更新,并且无需修改slug字段。
希望这有所帮助。

谢谢!当我写“从头开始”时,完全忘记了它。 - jasisz
但这不会将id保存到slug中。它只会将标题转换为slug格式,而对于OP的示例来说,许多标题可能相同。这就是他想要添加唯一id的原因,以便slug不会引发重复异常。 - Algorithmatic

3
你不应该在slug字段中使用id。这样做的两个主要原因是:
  1. 它会导致你遇到问题
  2. 这是数据库中冗余的数据 - id已经存储了
但是有一个简单的解决方案:slug只是用来通过slug查找行的,而你已经有了ID,所以你应该这样做:
class DealManager(models.Manager):
    def get_by_slug(slug):
       return self.get(id=slug.rsplit('-', 1)[1])


class Deal(models.Model):
    product = models.ForeignKey(Product)

    objects = DealManager()

    @property
    def slug(self):
        return slugify(f'{self.name}-f{self.id}')

在您的视图或任何需要根据slug检索项目的地方,您只需执行Deal.objects.get_by_slug(slug)即可。该函数将返回相应的对象。

2

我知道这可能不完全适用于你的情况,但我记得遇到过类似的情况。我想我的模型中有一个created_at字段,它具有auto_now=True或类似的属性。

我的slug看起来会像这样

self.slug = '%s-%s' %(
        slugify(self.title),
        self.created_at
)

或者您可以拥有。
self.slug = '%s-%s' %(
        slugify(self.title),
        datetime.datetime.now()
)

请确保slug的max_length足够长,可以包含完整的created_at时间和title,这样就不会出现非唯一或超过最大长度的异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接