Сигнальный крючок pre_save()
действительно является отличным местом для проведения усадки для большого количества моделей.Хитрость заключается в том, чтобы узнать, какие модели требуют создания слагов, какое поле должно быть основой для значения слагов.
Для этого я использую декоратор классов, позволяющий помечать модели для генерации слагов, ина каком поле его основывать:
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)
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.Это потребует проверки исключений целостности в транзакции или использования рандомизированного значения в слаге из достаточно большого пула, чтобы сделать коллизии маловероятными.Проверка исключения целостности может быть выполнена только в методе save()
, а не в перехватчиках сигналов.