mirror of
https://github.com/SectorLabs/django-localized-fields.git
synced 2025-04-24 19:32:53 +03:00
118 lines
3.8 KiB
Python
118 lines
3.8 KiB
Python
from datetime import datetime
|
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
from django.utils.text import slugify
|
|
|
|
from ..mixins import AtomicSlugRetryMixin
|
|
from ..util import get_language_codes
|
|
from ..value import LocalizedValue
|
|
from .autoslug_field import LocalizedAutoSlugField
|
|
|
|
|
|
class LocalizedUniqueSlugField(LocalizedAutoSlugField):
|
|
"""Automatically provides slugs for a localized field upon saving.".
|
|
|
|
An improved version of :see:LocalizedAutoSlugField,
|
|
which adds:
|
|
|
|
- Concurrency safety
|
|
- Improved performance
|
|
|
|
When in doubt, use this over :see:LocalizedAutoSlugField.
|
|
Inherit from :see:AtomicSlugRetryMixin in your model to
|
|
make this field work properly.
|
|
|
|
By default, this creates a new slug if the field(s) specified
|
|
in `populate_from` are changed. Set `immutable=True` to get
|
|
immutable slugs.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Initializes a new instance of :see:LocalizedUniqueSlugField."""
|
|
|
|
kwargs["uniqueness"] = kwargs.pop("uniqueness", get_language_codes())
|
|
|
|
self.immutable = kwargs.pop("immutable", False)
|
|
|
|
super(LocalizedUniqueSlugField, self).__init__(*args, **kwargs)
|
|
|
|
self.populate_from = kwargs.pop("populate_from")
|
|
self.include_time = kwargs.pop("include_time", False)
|
|
|
|
def deconstruct(self):
|
|
"""Deconstructs the field into something the database can store."""
|
|
|
|
name, path, args, kwargs = super(
|
|
LocalizedUniqueSlugField, self
|
|
).deconstruct()
|
|
|
|
kwargs["populate_from"] = self.populate_from
|
|
kwargs["include_time"] = self.include_time
|
|
|
|
if self.immutable is True:
|
|
kwargs["immutable"] = self.immutable
|
|
|
|
return name, path, args, kwargs
|
|
|
|
def pre_save(self, instance, add: bool):
|
|
"""Ran just before the model is saved, allows us to built the slug.
|
|
|
|
Arguments:
|
|
instance:
|
|
The model that is being saved.
|
|
|
|
add:
|
|
Indicates whether this is a new entry
|
|
to the database or an update.
|
|
|
|
Returns:
|
|
The localized slug that was generated.
|
|
"""
|
|
|
|
if not isinstance(instance, AtomicSlugRetryMixin):
|
|
raise ImproperlyConfigured(
|
|
(
|
|
"Model '%s' does not inherit from AtomicSlugRetryMixin. "
|
|
"Without this, the LocalizedUniqueSlugField will not work."
|
|
)
|
|
% type(instance).__name__
|
|
)
|
|
|
|
slugs = LocalizedValue()
|
|
|
|
for lang_code, value in self._get_populate_values(instance):
|
|
if not value:
|
|
continue
|
|
|
|
slug = slugify(value, allow_unicode=True)
|
|
|
|
current_slug = getattr(instance, self.name).get(lang_code)
|
|
if current_slug and self.immutable:
|
|
slugs.set(lang_code, current_slug)
|
|
continue
|
|
|
|
# verify whether it's needed to re-generate a slug,
|
|
# if not, re-use the same slug
|
|
if instance.pk is not None:
|
|
if current_slug is not None:
|
|
current_slug_end_index = current_slug.rfind("-")
|
|
stripped_slug = current_slug[0:current_slug_end_index]
|
|
if slug == stripped_slug:
|
|
slugs.set(lang_code, current_slug)
|
|
continue
|
|
|
|
if self.include_time:
|
|
slug += "-%d" % datetime.now().microsecond
|
|
|
|
retries = getattr(instance, "retries", 0)
|
|
if retries > 0:
|
|
# do not add another - if we already added time
|
|
if not self.include_time:
|
|
slug += "-"
|
|
slug += "%d" % retries
|
|
|
|
slugs.set(lang_code, slug)
|
|
|
|
setattr(instance, self.name, slugs)
|
|
return slugs
|