mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-10-28 01:48:57 +03:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 1317023160 | ||
|  | ca6b1c88fa | ||
|  | 64c3c06612 | ||
|  | b121dfc2d7 | ||
|  | d529da8886 | ||
|  | ca879087ea | ||
|  | 302a64a02c | ||
|  | bb11253207 | ||
|  | 5db87763fb | 
							
								
								
									
										48
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								README.rst
									
									
									
									
									
								
							| @@ -146,14 +146,14 @@ At the moment, it is not possible to select two languages to be marked as requir | ||||
|  | ||||
|     .. code-block:: python | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(required=True) | ||||
|  | ||||
| * Make all languages optional: | ||||
|  | ||||
|     .. code-block:: python | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(null=True) | ||||
|  | ||||
| **Uniqueness** | ||||
| @@ -164,7 +164,7 @@ By default the values stored in a ``LocalizedField`` are *not unique*. You can e | ||||
|  | ||||
|     .. code-block:: python | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(uniqueness=['en', 'ro']) | ||||
|  | ||||
| * Enforce uniqueness for **all** languages: | ||||
| @@ -173,14 +173,14 @@ By default the values stored in a ``LocalizedField`` are *not unique*. You can e | ||||
|  | ||||
|         from localized_fields import get_language_codes | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(uniqueness=get_language_codes()) | ||||
|  | ||||
| * Enforce uniqueness for one ore more languages **together** (similar to Django's ``unique_together``): | ||||
|  | ||||
|     .. code-block:: python | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(uniqueness=[('en', 'ro')]) | ||||
|  | ||||
| * Enforce uniqueness for **all** languages **together**: | ||||
| @@ -189,7 +189,7 @@ By default the values stored in a ``LocalizedField`` are *not unique*. You can e | ||||
|  | ||||
|         from localized_fields import get_language_codes | ||||
|  | ||||
|         class MyModel(models.Model): | ||||
|         class MyModel(LocalizedModel): | ||||
|             title = LocalizedField(uniqueness=[(*get_language_codes())]) | ||||
|  | ||||
|  | ||||
| @@ -197,19 +197,29 @@ Other fields | ||||
| ^^^^^^^^^^^^ | ||||
| Besides ``LocalizedField``, there's also: | ||||
|  | ||||
| * ``LocalizedMagicSlugField`` | ||||
| * ``LocalizedUniqueSlugField`` | ||||
|     Successor of ``LocalizedAutoSlugField`` that fixes concurrency issues and enforces | ||||
|     uniqueness of slugs on a database level. Usage is the exact same: | ||||
|  | ||||
|           .. code-block:: python | ||||
|  | ||||
|               from localized_fields.models import LocalizedModel | ||||
|               from localized_fields.fields import (LocalizedField, | ||||
|                                                    LocalizedMagicSlugField) | ||||
|               from localized_fields import (LocalizedModel, | ||||
|                                             AtomicSlugRetryMixin, | ||||
|                                             LocalizedField, | ||||
|                                             LocalizedUniqueSlugField) | ||||
|  | ||||
|               class MyModel(LocalizedModel): | ||||
|               class MyModel(AtomicSlugRetryMixin, LocalizedModel): | ||||
|                    title = LocalizedField() | ||||
|                    slug = LocalizedMagicSlugField(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`` | ||||
|      Automatically creates a slug for every language from the specified field. | ||||
| @@ -218,15 +228,15 @@ Besides ``LocalizedField``, there's also: | ||||
|  | ||||
|           .. code-block:: python | ||||
|  | ||||
|               from localized_fields.models import LocalizedModel | ||||
|               from localized_fields.fields import (LocalizedField, | ||||
|                                                    LocalizedAutoSlugField) | ||||
|               from localized_fields import (LocalizedModel, | ||||
|                                             LocalizedField, | ||||
|                                             LocalizedUniqueSlugField) | ||||
|  | ||||
|               class MyModel(LocalizedModel): | ||||
|                    title = LocalizedField() | ||||
|                    slug = LocalizedAutoSlugField(populate_from='title') | ||||
|  | ||||
|      This implementation is **NOT** concurrency safe, prefer ``LocalizedMagicSlugField``. | ||||
|      This implementation is **NOT** concurrency safe, prefer ``LocalizedUniqueSlugField``. | ||||
|  | ||||
| * ``LocalizedBleachField`` | ||||
|      Automatically bleaches the content of the field. | ||||
| @@ -236,9 +246,9 @@ Besides ``LocalizedField``, there's also: | ||||
|  | ||||
|            .. code-block:: python | ||||
|  | ||||
|               from localized_fields.models import LocalizedModel | ||||
|               from localized_fields.fields import (LocalizedField, | ||||
|                                                    LocalizedBleachField) | ||||
|               from localized_fields import (LocalizedModel, | ||||
|                                             LocalizedField, | ||||
|                                             LocalizedBleachField) | ||||
|  | ||||
|               class MyModel(LocalizedModel): | ||||
|                    title = LocalizedField() | ||||
|   | ||||
| @@ -1,18 +1,20 @@ | ||||
| from .util import get_language_codes | ||||
| from .forms import LocalizedFieldForm, LocalizedFieldWidget | ||||
| from .fields import (LocalizedField, LocalizedBleachField, | ||||
|                      LocalizedAutoSlugField, LocalizedMagicSlugField) | ||||
| from .localized_value import LocalizedValue | ||||
|                      LocalizedAutoSlugField, LocalizedUniqueSlugField) | ||||
| from .mixins import AtomicSlugRetryMixin | ||||
| from .models import LocalizedModel | ||||
| from .localized_value import LocalizedValue | ||||
|  | ||||
| __all__ = [ | ||||
|     'get_language_codes', | ||||
|     'LocalizedField', | ||||
|     'LocalizedValue', | ||||
|     'LocalizedAutoSlugField', | ||||
|     'LocalizedMagicSlugField', | ||||
|     'LocalizedUniqueSlugField', | ||||
|     'LocalizedBleachField', | ||||
|     'LocalizedFieldWidget', | ||||
|     'LocalizedFieldForm', | ||||
|     'LocalizedModel' | ||||
|     'LocalizedModel', | ||||
|     'AtomicSlugRetryMixin' | ||||
| ] | ||||
|   | ||||
| @@ -34,14 +34,14 @@ def _get_backend_base(): | ||||
|             '\'%s\' is not a valid database back-end.' | ||||
|             ' The module does not define a DatabaseWrapper class.' | ||||
|             ' Check the value of LOCALIZED_FIELDS_DB_BACKEND_BASE.' | ||||
|         )) | ||||
|         ) % base_class_name) | ||||
|  | ||||
|     if isinstance(base_class, Psycopg2DatabaseWrapper): | ||||
|         raise ImproperlyConfigured(( | ||||
|             '\'%s\' is not a valid database back-end.' | ||||
|             ' It does inherit from the PostgreSQL back-end.' | ||||
|             ' Check the value of LOCALIZED_FIELDS_DB_BACKEND_BASE.' | ||||
|         )) | ||||
|         ) % base_class_name) | ||||
|  | ||||
|     return base_class | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| from .localized_field import LocalizedField | ||||
| from .localized_autoslug_field import LocalizedAutoSlugField | ||||
| from .localized_magicslug_field import LocalizedMagicSlugField | ||||
| from .localized_uniqueslug_field import LocalizedUniqueSlugField | ||||
| from .localized_bleach_field import LocalizedBleachField | ||||
|  | ||||
|  | ||||
| __all__ = [ | ||||
|     'LocalizedField', | ||||
|     'LocalizedAutoSlugField', | ||||
|     'LocalizedMagicSlugField', | ||||
|     'LocalizedUniqueSlugField', | ||||
|     'LocalizedBleachField', | ||||
| ] | ||||
|   | ||||
| @@ -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.""" | ||||
|  | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| from django.conf import settings | ||||
| from django.utils.text import slugify | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| 
 | ||||
| from ..util import get_language_codes | ||||
| from ..mixins import AtomicSlugRetryMixin | ||||
| from ..localized_value import LocalizedValue | ||||
| from .localized_autoslug_field import LocalizedAutoSlugField | ||||
| from ..util import get_language_codes | ||||
| 
 | ||||
| from datetime import datetime | ||||
| 
 | ||||
| 
 | ||||
| class LocalizedMagicSlugField(LocalizedAutoSlugField): | ||||
| class LocalizedUniqueSlugField(LocalizedAutoSlugField): | ||||
|     """Automatically provides slugs for a localized | ||||
|     field upon saving." | ||||
| 
 | ||||
| @@ -17,19 +21,34 @@ class LocalizedMagicSlugField(LocalizedAutoSlugField): | ||||
|         - Improved performance | ||||
| 
 | ||||
|     When in doubt, use this over :see:LocalizedAutoSlugField. | ||||
|     Inherit from :see:AtomicSlugRetryMixin in your model to | ||||
|     make this field work properly. | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         """Initializes a new instance of :see:LocalizedMagicSlugField.""" | ||||
|         """Initializes a new instance of :see:LocalizedUniqueSlugField.""" | ||||
| 
 | ||||
|         self.populate_from = kwargs.pop('populate_from') | ||||
|         kwargs['uniqueness'] = kwargs.pop('uniqueness', get_language_codes()) | ||||
| 
 | ||||
|         super(LocalizedAutoSlugField, self).__init__( | ||||
|         super(LocalizedUniqueSlugField, self).__init__( | ||||
|             *args, | ||||
|             **kwargs | ||||
|         ) | ||||
| 
 | ||||
|         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 | ||||
|         the slug. | ||||
| @@ -46,6 +65,12 @@ class LocalizedMagicSlugField(LocalizedAutoSlugField): | ||||
|             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, _ in settings.LANGUAGES: | ||||
| @@ -59,6 +84,9 @@ class LocalizedMagicSlugField(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 | ||||
| 
 | ||||
| @@ -76,6 +76,21 @@ class LocalizedValue: | ||||
|  | ||||
|         return value or '' | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         """Compares :paramref:self to :paramref:other for | ||||
|         equality. | ||||
|  | ||||
|         Returns: | ||||
|             True when :paramref:self is equal to :paramref:other. | ||||
|             And False when they are not. | ||||
|         """ | ||||
|  | ||||
|         for lang_code, _ in settings.LANGUAGES: | ||||
|             if self.get(lang_code) != other.get(lang_code): | ||||
|                 return False | ||||
|  | ||||
|         return True | ||||
|  | ||||
|     def __repr__(self):  # pragma: no cover | ||||
|         """Gets a textual representation of this object.""" | ||||
|  | ||||
|   | ||||
							
								
								
									
										38
									
								
								localized_fields/mixins.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								localized_fields/mixins.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| from django.db import transaction | ||||
| from django.conf import settings | ||||
| from django.db.utils import IntegrityError | ||||
|  | ||||
|  | ||||
| class AtomicSlugRetryMixin: | ||||
|     """Makes :see:LocalizedUniqueSlugField work by retrying upon | ||||
|     violation of the UNIQUE constraint.""" | ||||
|  | ||||
|     def save(self, *args, **kwargs): | ||||
|         """Saves this model instance to the database.""" | ||||
|  | ||||
|         max_retries = getattr( | ||||
|             settings, | ||||
|             'LOCALIZED_FIELDS_MAX_RETRIES', | ||||
|             100 | ||||
|         ) | ||||
|  | ||||
|         if not hasattr(self, 'retries'): | ||||
|             self.retries = 0 | ||||
|  | ||||
|         with transaction.atomic(): | ||||
|             try: | ||||
|                 return super().save(*args, **kwargs) | ||||
|             except IntegrityError as ex: | ||||
|                 # this is as retarded as it looks, there's no | ||||
|                 # way we can put the retry logic inside the slug | ||||
|                 # field class... we can also not only catch exceptions | ||||
|                 # that apply to slug fields... so yea.. this is as | ||||
|                 # retarded as it gets... i am sorry :( | ||||
|                 if 'slug' not in str(ex): | ||||
|                     raise ex | ||||
|  | ||||
|                 if self.retries >= max_retries: | ||||
|                     raise ex | ||||
|  | ||||
|         self.retries += 1 | ||||
|         return self.save() | ||||
| @@ -34,33 +34,3 @@ class LocalizedModel(models.Model): | ||||
|                     value = LocalizedValue() | ||||
|  | ||||
|             setattr(self, field.name, value) | ||||
|  | ||||
|     def save(self, *args, **kwargs): | ||||
|         """Saves this model instance to the database.""" | ||||
|  | ||||
|         max_retries = getattr( | ||||
|             settings, | ||||
|             'LOCALIZED_FIELDS_MAX_RETRIES', | ||||
|             100 | ||||
|         ) | ||||
|  | ||||
|         if not hasattr(self, 'retries'): | ||||
|             self.retries = 0 | ||||
|  | ||||
|         with transaction.atomic(): | ||||
|             try: | ||||
|                 return super(LocalizedModel, self).save(*args, **kwargs) | ||||
|             except IntegrityError as ex: | ||||
|                 # this is as retarded as it looks, there's no | ||||
|                 # way we can put the retry logic inside the slug | ||||
|                 # field class... we can also not only catch exceptions | ||||
|                 # that apply to slug fields... so yea.. this is as | ||||
|                 # retarded as it gets... i am sorry :( | ||||
|                 if 'slug' not in str(ex): | ||||
|                     raise ex | ||||
|  | ||||
|                 if self.retries >= max_retries: | ||||
|                     raise ex | ||||
|  | ||||
|         self.retries += 1 | ||||
|         return self.save() | ||||
|   | ||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: | ||||
|  | ||||
| setup( | ||||
|     name='django-localized-fields', | ||||
|     version='2.5', | ||||
|     version='2.8', | ||||
|     packages=find_packages(), | ||||
|     include_package_data=True, | ||||
|     license='MIT License', | ||||
|   | ||||
| @@ -2,7 +2,7 @@ from django.db import connection, migrations | ||||
| from django.db.migrations.executor import MigrationExecutor | ||||
| from django.contrib.postgres.operations import HStoreExtension | ||||
|  | ||||
| from localized_fields import LocalizedModel | ||||
| from localized_fields import LocalizedModel, AtomicSlugRetryMixin | ||||
|  | ||||
|  | ||||
| def define_fake_model(name='TestModel', fields=None): | ||||
| @@ -14,7 +14,7 @@ def define_fake_model(name='TestModel', fields=None): | ||||
|  | ||||
|     if fields: | ||||
|         attributes.update(fields) | ||||
|     model = type(name, (LocalizedModel,), attributes) | ||||
|     model = type(name, (AtomicSlugRetryMixin,LocalizedModel,), attributes) | ||||
|  | ||||
|     return model | ||||
|  | ||||
|   | ||||
| @@ -100,6 +100,19 @@ class LocalizedValueTestCase(TestCase): | ||||
|             translation.activate(language) | ||||
|             assert str(localized_value) == value | ||||
|  | ||||
|     @staticmethod | ||||
|     def test_eq(): | ||||
|         """Tests whether the __eq__ operator | ||||
|         of :see:LocalizedValue works properly.""" | ||||
|  | ||||
|         a = LocalizedValue({'en': 'a', 'ar': 'b'}) | ||||
|         b = LocalizedValue({'en': 'a', 'ar': 'b'}) | ||||
|  | ||||
|         assert a == b | ||||
|  | ||||
|         b.en = 'b' | ||||
|         assert a != b | ||||
|  | ||||
|     @staticmethod | ||||
|     def test_str_fallback(): | ||||
|         """Tests whether the :see:LocalizedValue | ||||
|   | ||||
| @@ -5,7 +5,7 @@ from django.db.utils import IntegrityError | ||||
| from django.utils.text import slugify | ||||
|  | ||||
| from localized_fields import (LocalizedField, LocalizedAutoSlugField, | ||||
|                               LocalizedMagicSlugField) | ||||
|                               LocalizedUniqueSlugField) | ||||
|  | ||||
| from .fake_model import get_fake_model | ||||
|  | ||||
| @@ -31,10 +31,10 @@ class LocalizedSlugFieldTestCase(TestCase): | ||||
|         ) | ||||
|  | ||||
|         cls.MagicSlugModel = get_fake_model( | ||||
|             'LocalizedMagicSlugFieldTestModel', | ||||
|             'LocalizedUniqueSlugFieldTestModel', | ||||
|             { | ||||
|                 'title': LocalizedField(), | ||||
|                 'slug': LocalizedMagicSlugField(populate_from='title') | ||||
|                 'slug': LocalizedUniqueSlugField(populate_from='title') | ||||
|             } | ||||
|         ) | ||||
|  | ||||
| @@ -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,16 +112,16 @@ class LocalizedSlugFieldTestCase(TestCase): | ||||
|         cls._test_deconstruct(LocalizedAutoSlugField) | ||||
|  | ||||
|     @classmethod | ||||
|     def test_deconstruct_magic(cls): | ||||
|         cls._test_deconstruct(LocalizedMagicSlugField) | ||||
|     def test_deconstruct_unique(cls): | ||||
|         cls._test_deconstruct(LocalizedUniqueSlugField) | ||||
|  | ||||
|     @classmethod | ||||
|     def test_formfield_auto(cls): | ||||
|         cls._test_formfield(LocalizedAutoSlugField) | ||||
|  | ||||
|     @classmethod | ||||
|     def test_formfield_magic(cls): | ||||
|         cls._test_formfield(LocalizedMagicSlugField) | ||||
|     def test_formfield_unique(cls): | ||||
|         cls._test_formfield(LocalizedUniqueSlugField) | ||||
|  | ||||
|     @staticmethod | ||||
|     def _test_populate(model): | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user