From ca6b1c88fa2f863712f8ac5f36a6d74fc4a98781 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Wed, 15 Feb 2017 19:13:19 +0200 Subject: [PATCH] Add option include_time to LocalizedUniqueSlugField --- README.rst | 9 ++++ .../fields/localized_autoslug_field.py | 6 +++ .../fields/localized_uniqueslug_field.py | 17 ++++++++ tests/test_localized_slug_fields.py | 41 ++++++++++++++----- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index b00d989..7d2b42c 100644 --- a/README.rst +++ b/README.rst @@ -212,6 +212,15 @@ Besides ``LocalizedField``, there's also: title = LocalizedField() slug = LocalizedUniqueSlugField(populate_from='title') + By setting the option ``include_time=True`` + + .. code-block:: python + + slug = LocalizedUniqueSlugField(populate_from='title', include_time=True) + + You can instruct the field to include a part of the current time into + the resulting slug. This is useful if you're running into a lot of collisions. + * ``LocalizedAutoSlugField`` Automatically creates a slug for every language from the specified field. diff --git a/localized_fields/fields/localized_autoslug_field.py b/localized_fields/fields/localized_autoslug_field.py index 3cf9d5e..8ce26db 100644 --- a/localized_fields/fields/localized_autoslug_field.py +++ b/localized_fields/fields/localized_autoslug_field.py @@ -1,4 +1,5 @@ from typing import Callable +from datetime import datetime from django import forms from django.conf import settings @@ -16,6 +17,7 @@ class LocalizedAutoSlugField(LocalizedField): """Initializes a new instance of :see:LocalizedAutoSlugField.""" self.populate_from = kwargs.pop('populate_from', None) + self.include_time = kwargs.pop('include_time', False) super(LocalizedAutoSlugField, self).__init__( *args, @@ -30,6 +32,7 @@ class LocalizedAutoSlugField(LocalizedField): LocalizedAutoSlugField, self).deconstruct() kwargs['populate_from'] = self.populate_from + kwargs['include_time'] = self.include_time return name, path, args, kwargs def formfield(self, **kwargs): @@ -76,6 +79,9 @@ class LocalizedAutoSlugField(LocalizedField): if not value: continue + if self.include_time: + value += '-%s' % datetime.now().microsecond + def is_unique(slug: str, language: str) -> bool: """Gets whether the specified slug is unique.""" diff --git a/localized_fields/fields/localized_uniqueslug_field.py b/localized_fields/fields/localized_uniqueslug_field.py index 2ff468e..e514473 100644 --- a/localized_fields/fields/localized_uniqueslug_field.py +++ b/localized_fields/fields/localized_uniqueslug_field.py @@ -7,6 +7,8 @@ from ..mixins import AtomicSlugRetryMixin from ..localized_value import LocalizedValue from .localized_autoslug_field import LocalizedAutoSlugField +from datetime import datetime + class LocalizedUniqueSlugField(LocalizedAutoSlugField): """Automatically provides slugs for a localized @@ -34,6 +36,18 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField): ) self.populate_from = kwargs.pop('populate_from') + self.use_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 + return name, path, args, kwargs def pre_save(self, instance, add: bool): """Ran just before the model is saved, allows us to built @@ -70,6 +84,9 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField): continue slug = slugify(value, allow_unicode=True) + if self.include_time: + slug += '-%d' % datetime.now().microsecond + if instance.retries > 0: slug += '-%d' % instance.retries diff --git a/tests/test_localized_slug_fields.py b/tests/test_localized_slug_fields.py index 90f345d..5455369 100644 --- a/tests/test_localized_slug_fields.py +++ b/tests/test_localized_slug_fields.py @@ -43,7 +43,7 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_populate(cls.AutoSlugModel) @classmethod - def test_populate_magic(cls): + def test_populate_unique(cls): cls._test_populate(cls.MagicSlugModel) @classmethod @@ -51,7 +51,7 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_populate_multiple_languages(cls.AutoSlugModel) @classmethod - def test_populate_multiple_languages_magic(cls): + def test_populate_multiple_languages_unique(cls): cls._test_populate_multiple_languages(cls.MagicSlugModel) @classmethod @@ -59,14 +59,35 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_unique_slug(cls.AutoSlugModel) @classmethod - def test_unique_slug_magic(cls): + def test_unique_slug_unique(cls): cls._test_unique_slug(cls.MagicSlugModel) - def test_unique_slug_magic_max_retries(self): - """Tests whether the magic slug implementation doesn't + @staticmethod + def test_unique_slug_with_time(): + """Tests whether the primary key is included in + the slug when the 'use_pk' option is enabled.""" + + title = 'myuniquetitle' + + PkModel = get_fake_model( + 'PkModel', + { + 'title': LocalizedField(), + 'slug': LocalizedUniqueSlugField(populate_from='title', include_time=True) + } + ) + + obj = PkModel() + obj.title.en = title + obj.save() + + assert obj.slug.en.startswith('%s-' % title) + + def test_unique_slug_unique_max_retries(self): + """Tests whether the unique slug implementation doesn't try to find a slug forever and gives up after a while.""" - title = 'mymagictitle' + title = 'myuniquetitle' obj = self.MagicSlugModel() obj.title.en = title @@ -83,7 +104,7 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_unique_slug_utf(cls.AutoSlugModel) @classmethod - def test_unique_slug_utf_magic(cls): + def test_unique_slug_utf_unique(cls): cls._test_unique_slug_utf(cls.MagicSlugModel) @classmethod @@ -91,7 +112,7 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_deconstruct(LocalizedAutoSlugField) @classmethod - def test_deconstruct_magic(cls): + def test_deconstruct_unique(cls): cls._test_deconstruct(LocalizedUniqueSlugField) @classmethod @@ -99,7 +120,7 @@ class LocalizedSlugFieldTestCase(TestCase): cls._test_formfield(LocalizedAutoSlugField) @classmethod - def test_formfield_magic(cls): + def test_formfield_unique(cls): cls._test_formfield(LocalizedUniqueSlugField) @staticmethod @@ -130,7 +151,7 @@ class LocalizedSlugFieldTestCase(TestCase): def _test_unique_slug(model): """Tests whether unique slugs are properly generated.""" - title = 'mymagictitle' + title = 'myuniquetitle' obj = model() obj.title.en = title