7 Commits
v2.7 ... v2.9.2

Author SHA1 Message Date
Swen Kooij
b9a4d3be2c Bump version number to 2.9.2 2017-02-16 10:20:09 +02:00
Swen Kooij
6d7a937eac Don't regenerate slug if not needed 2017-02-16 10:13:10 +02:00
Swen Kooij
2e9b83e49b Bump version to 2.9.1 2017-02-16 09:36:34 +02:00
Swen Kooij
679dcafef6 Pop kwargs after calling super constructor 2017-02-16 09:36:26 +02:00
Swen Kooij
ad2ef34546 Bump version number to 2.9 2017-02-15 19:29:29 +02:00
Swen Kooij
1317023160 Bump version number to 2.8 2017-02-15 19:13:40 +02:00
Swen Kooij
ca6b1c88fa Add option include_time to LocalizedUniqueSlugField 2017-02-15 19:13:19 +02:00
5 changed files with 109 additions and 14 deletions

View File

@@ -212,6 +212,15 @@ Besides ``LocalizedField``, there's also:
title = LocalizedField() title = LocalizedField()
slug = LocalizedUniqueSlugField(populate_from='title') 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`` * ``LocalizedAutoSlugField``
Automatically creates a slug for every language from the specified field. Automatically creates a slug for every language from the specified field.

View File

@@ -1,4 +1,5 @@
from typing import Callable from typing import Callable
from datetime import datetime
from django import forms from django import forms
from django.conf import settings from django.conf import settings
@@ -16,6 +17,7 @@ class LocalizedAutoSlugField(LocalizedField):
"""Initializes a new instance of :see:LocalizedAutoSlugField.""" """Initializes a new instance of :see:LocalizedAutoSlugField."""
self.populate_from = kwargs.pop('populate_from', None) self.populate_from = kwargs.pop('populate_from', None)
self.include_time = kwargs.pop('include_time', False)
super(LocalizedAutoSlugField, self).__init__( super(LocalizedAutoSlugField, self).__init__(
*args, *args,
@@ -30,6 +32,7 @@ class LocalizedAutoSlugField(LocalizedField):
LocalizedAutoSlugField, self).deconstruct() LocalizedAutoSlugField, self).deconstruct()
kwargs['populate_from'] = self.populate_from kwargs['populate_from'] = self.populate_from
kwargs['include_time'] = self.include_time
return name, path, args, kwargs return name, path, args, kwargs
def formfield(self, **kwargs): def formfield(self, **kwargs):
@@ -76,6 +79,9 @@ class LocalizedAutoSlugField(LocalizedField):
if not value: if not value:
continue continue
if self.include_time:
value += '-%s' % datetime.now().microsecond
def is_unique(slug: str, language: str) -> bool: def is_unique(slug: str, language: str) -> bool:
"""Gets whether the specified slug is unique.""" """Gets whether the specified slug is unique."""

View File

@@ -1,3 +1,5 @@
from datetime import datetime
from django.conf import settings from django.conf import settings
from django.utils.text import slugify from django.utils.text import slugify
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@@ -34,6 +36,18 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
) )
self.populate_from = kwargs.pop('populate_from') 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
return name, path, args, kwargs
def pre_save(self, instance, add: bool): def pre_save(self, instance, add: bool):
"""Ran just before the model is saved, allows us to built """Ran just before the model is saved, allows us to built
@@ -70,8 +84,25 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
continue continue
slug = slugify(value, allow_unicode=True) slug = slugify(value, allow_unicode=True)
# verify whether it's needed to re-generate a slug,
# if not, re-use the same slug
if instance.pk is not None:
current_slug = getattr(instance, self.name).get(lang_code)
if current_slug is not None:
stripped_slug = current_slug[0:current_slug.rfind('-')]
if slug == stripped_slug:
slugs.set(lang_code, current_slug)
continue
if self.include_time:
slug += '-%d' % datetime.now().microsecond
if instance.retries > 0: if instance.retries > 0:
slug += '-%d' % instance.retries # do not add another - if we already added time
if not self.include_time:
slug += '-'
slug += '%d' % instance.retries
slugs.set(lang_code, slug) slugs.set(lang_code, slug)

View File

@@ -7,7 +7,7 @@ with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme:
setup( setup(
name='django-localized-fields', name='django-localized-fields',
version='2.7', version='2.9.2',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
license='MIT License', license='MIT License',

View File

@@ -1,11 +1,12 @@
import copy
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from django.utils.text import slugify
from localized_fields import (LocalizedField, LocalizedAutoSlugField, from localized_fields import (LocalizedField, LocalizedAutoSlugField,
LocalizedUniqueSlugField) LocalizedUniqueSlugField)
from django.utils.text import slugify
from .fake_model import get_fake_model from .fake_model import get_fake_model
@@ -43,7 +44,7 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_populate(cls.AutoSlugModel) cls._test_populate(cls.AutoSlugModel)
@classmethod @classmethod
def test_populate_magic(cls): def test_populate_unique(cls):
cls._test_populate(cls.MagicSlugModel) cls._test_populate(cls.MagicSlugModel)
@classmethod @classmethod
@@ -51,7 +52,7 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_populate_multiple_languages(cls.AutoSlugModel) cls._test_populate_multiple_languages(cls.AutoSlugModel)
@classmethod @classmethod
def test_populate_multiple_languages_magic(cls): def test_populate_multiple_languages_unique(cls):
cls._test_populate_multiple_languages(cls.MagicSlugModel) cls._test_populate_multiple_languages(cls.MagicSlugModel)
@classmethod @classmethod
@@ -59,14 +60,62 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_unique_slug(cls.AutoSlugModel) cls._test_unique_slug(cls.AutoSlugModel)
@classmethod @classmethod
def test_unique_slug_magic(cls): def test_unique_slug_unique(cls):
cls._test_unique_slug(cls.MagicSlugModel) cls._test_unique_slug(cls.MagicSlugModel)
def test_unique_slug_magic_max_retries(self): @staticmethod
"""Tests whether the magic slug implementation doesn't 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)
@classmethod
def test_uniue_slug_no_change(cls):
"""Tests whether slugs are not re-generated if not needed."""
NoChangeSlugModel = get_fake_model(
'NoChangeSlugModel',
{
'title': LocalizedField(),
'slug': LocalizedUniqueSlugField(populate_from='title', include_time=True)
}
)
title = 'myuniquetitle'
obj = NoChangeSlugModel()
obj.title.en = title
obj.title.nl = title
obj.save()
old_slug_en = copy.deepcopy(obj.slug.en)
old_slug_nl = copy.deepcopy(obj.slug.nl)
obj.title.nl += 'beer'
obj.save()
assert old_slug_en == obj.slug.en
assert old_slug_nl != obj.slug.nl
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.""" try to find a slug forever and gives up after a while."""
title = 'mymagictitle' title = 'myuniquetitle'
obj = self.MagicSlugModel() obj = self.MagicSlugModel()
obj.title.en = title obj.title.en = title
@@ -83,7 +132,7 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_unique_slug_utf(cls.AutoSlugModel) cls._test_unique_slug_utf(cls.AutoSlugModel)
@classmethod @classmethod
def test_unique_slug_utf_magic(cls): def test_unique_slug_utf_unique(cls):
cls._test_unique_slug_utf(cls.MagicSlugModel) cls._test_unique_slug_utf(cls.MagicSlugModel)
@classmethod @classmethod
@@ -91,7 +140,7 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_deconstruct(LocalizedAutoSlugField) cls._test_deconstruct(LocalizedAutoSlugField)
@classmethod @classmethod
def test_deconstruct_magic(cls): def test_deconstruct_unique(cls):
cls._test_deconstruct(LocalizedUniqueSlugField) cls._test_deconstruct(LocalizedUniqueSlugField)
@classmethod @classmethod
@@ -99,7 +148,7 @@ class LocalizedSlugFieldTestCase(TestCase):
cls._test_formfield(LocalizedAutoSlugField) cls._test_formfield(LocalizedAutoSlugField)
@classmethod @classmethod
def test_formfield_magic(cls): def test_formfield_unique(cls):
cls._test_formfield(LocalizedUniqueSlugField) cls._test_formfield(LocalizedUniqueSlugField)
@staticmethod @staticmethod
@@ -130,7 +179,7 @@ class LocalizedSlugFieldTestCase(TestCase):
def _test_unique_slug(model): def _test_unique_slug(model):
"""Tests whether unique slugs are properly generated.""" """Tests whether unique slugs are properly generated."""
title = 'mymagictitle' title = 'myuniquetitle'
obj = model() obj = model()
obj.title.en = title obj.title.en = title