pre_save() signal hook 确实是处理大量模型的 slugification 的好地方。诀窍是要知道哪些模型需要生成 slug,哪些字段应该是 slug 值的基础。
为此我使用了一个类装饰器,它可以让我标记模型以进行自动 slug 生成,以及它基于什么字段:
from django.db import models
from django.dispatch import receiver
from django.utils.text import slugify
def autoslug(fieldname):
def decorator(model):
# some sanity checks first
assert hasattr(model, fieldname), f"Model has no field {fieldname!r}"
assert hasattr(model, "slug"), "Model is missing a slug field"
@receiver(models.signals.pre_save, sender=model, weak=False)
def generate_slug(sender, instance, *args, raw=False, **kwargs):
if not raw and not instance.slug:
source = getattr(instance, fieldname)
slug = slugify(source)
if slug: # not all strings result in a slug value
instance.slug = slug
return model
return decorator
这只会为特定模型注册一个信号处理程序,并允许您使用每个修饰的模型来改变源字段:
@autoslug("name")
class NamedModel(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField()
@autoslug("title")
class TitledModel(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
请注意,不会尝试生成 唯一 slug 值。这将需要检查事务中的完整性异常或使用来自足够大池的 slug 中的随机值以使冲突不太可能发生。完整性异常检查只能在save()方法中进行,不能在信号钩子中进行。