# Imports
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from .utils import unique_slugify
# Exports
__all__ = (
"AutoSlugField",
"UniqueAutoSlugField",
)
# Fields
[docs]class AutoSlugField(models.SlugField):
"""Automatically generate a slug from another field.
:param from_field: The field from which the slug will be populated. This field must exist within the same model.
:type from_field: str
:param slug_using: The function to use for creating the slug. Defaults to Django's implementation of ``slugify()``.
:type slug_using: callable
Example of use:
.. code-block:: python
class Project(models.Model):
title = models.CharField(max_length=128)
slug = AutoSlugField(from_field="title", max_length=128)
"""
description = _("Automatically generate a slug from another field.")
[docs] def __init__(self, *args, **kwargs):
"""Check for the presence of ``from_field`` and ``slug_using``."""
self.from_field = kwargs.pop("from_field", None)
if self.from_field is None:
raise ImproperlyConfigured("from_field is required by %s" % self.model.__class__.__name__)
self.slug_using = kwargs.pop("slug_using", slugify)
if not callable(self.slug_using):
raise ImproperlyConfigured("slug_using must be a callable for %s" % self.model.__class__.__name__)
super().__init__(*args, **kwargs)
[docs] def get_slug(self, model_instance):
"""Generate the slug.
:param model_instance: The current record (model instance).
:rtype: str
"""
try:
value = getattr(model_instance, self.from_field)
except AttributeError:
raise ImproperlyConfigured("from_field field does not exist: %s" % self.from_field)
callback = self.slug_using
return callback(value)
[docs] def pre_save(self, model_instance, add):
"""Generate the slug."""
return self.get_slug(model_instance)
[docs]class UniqueAutoSlugField(AutoSlugField):
"""Like :py:class:`superdjango.db.slug.fields.AutoSlugField`, but adds a unique constraint.
.. warning::
Note that this causes additional database queries which may impact performance.
"""
description = _("Automatically generate a unique slug from another field.")
[docs] def __init__(self, *args, **kwargs):
kwargs['unique'] = True
super().__init__(*args, **kwargs)
[docs] def get_slug(self, model_instance):
value = getattr(model_instance, self.from_field)
return unique_slugify(model_instance, value, callback=self.slug_using, field=self.name)