mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-10-30 10:38:58 +03:00 
			
		
		
		
	Support for slugging from multiple fields
This commit is contained in:
		| @@ -1,12 +1,14 @@ | |||||||
| from typing import Callable, Tuple | from typing import Callable, Tuple, Union | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  |  | ||||||
| from django import forms | from django import forms | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
|  | from django.utils import translation | ||||||
| from django.utils.text import slugify | from django.utils.text import slugify | ||||||
|  |  | ||||||
| from .field import LocalizedField | from .field import LocalizedField | ||||||
| from ..value import LocalizedValue | from ..value import LocalizedValue | ||||||
|  | from ..util import resolve_object_property | ||||||
|  |  | ||||||
|  |  | ||||||
| class LocalizedAutoSlugField(LocalizedField): | class LocalizedAutoSlugField(LocalizedField): | ||||||
| @@ -147,7 +149,7 @@ class LocalizedAutoSlugField(LocalizedField): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _get_populate_from_value(instance, field_name: str, language: str): |     def _get_populate_from_value(instance, field_name: Union[str, Tuple[str]], language: str): | ||||||
|         """Gets the value to create a slug from in the specified language. |         """Gets the value to create a slug from in the specified language. | ||||||
|  |  | ||||||
|         Arguments: |         Arguments: | ||||||
| @@ -164,5 +166,17 @@ class LocalizedAutoSlugField(LocalizedField): | |||||||
|             The text to generate a slug for. |             The text to generate a slug for. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         value = getattr(instance, field_name, None) |         def get_field_value(name): | ||||||
|         return value.get(language) |             value = resolve_object_property(instance, name) | ||||||
|  |             with translation.override(language): | ||||||
|  |                 return str(value) | ||||||
|  |  | ||||||
|  |         if isinstance(field_name, tuple) or isinstance(field_name, list): | ||||||
|  |             value = '-'.join([ | ||||||
|  |                 value | ||||||
|  |                 for value in [get_field_value(name) for name in field_name] | ||||||
|  |                 if value | ||||||
|  |             ]) | ||||||
|  |             return value | ||||||
|  |  | ||||||
|  |         return get_field_value(field_name) | ||||||
|   | |||||||
| @@ -19,3 +19,26 @@ def get_language_codes() -> List[str]: | |||||||
|         lang_code |         lang_code | ||||||
|         for lang_code, _ in settings.LANGUAGES |         for lang_code, _ in settings.LANGUAGES | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def resolve_object_property(obj, path: str): | ||||||
|  |     """Resolves the value of a property on an object. | ||||||
|  |  | ||||||
|  |     Is able to resolve nested properties. For example, | ||||||
|  |     a path can be specified: | ||||||
|  |  | ||||||
|  |         'other.beer.name' | ||||||
|  |  | ||||||
|  |     Raises: | ||||||
|  |         AttributeError: | ||||||
|  |             In case the property could not be resolved. | ||||||
|  |  | ||||||
|  |     Returns: | ||||||
|  |         The value of the specified property. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     value = obj | ||||||
|  |     for path_part in path.split('.'): | ||||||
|  |         value = getattr(value, path_part) | ||||||
|  |  | ||||||
|  |     return value | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import copy | import copy | ||||||
|  |  | ||||||
| from django import forms | from django import forms | ||||||
|  | from django.db import models | ||||||
| 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 | ||||||
| @@ -31,6 +32,7 @@ class LocalizedSlugFieldTestCase(TestCase): | |||||||
|             'LocalizedAutoSlugFieldTestModel', |             'LocalizedAutoSlugFieldTestModel', | ||||||
|             { |             { | ||||||
|                 'title': LocalizedField(), |                 'title': LocalizedField(), | ||||||
|  |                 'name': models.CharField(max_length=255), | ||||||
|                 'slug': LocalizedAutoSlugField(populate_from='title') |                 'slug': LocalizedAutoSlugField(populate_from='title') | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
| @@ -39,6 +41,7 @@ class LocalizedSlugFieldTestCase(TestCase): | |||||||
|             'LocalizedUniqueSlugFieldTestModel', |             'LocalizedUniqueSlugFieldTestModel', | ||||||
|             { |             { | ||||||
|                 'title': LocalizedField(), |                 'title': LocalizedField(), | ||||||
|  |                 'name': models.CharField(max_length=255), | ||||||
|                 'slug': LocalizedUniqueSlugField(populate_from='title') |                 'slug': LocalizedUniqueSlugField(populate_from='title') | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
| @@ -155,6 +158,22 @@ class LocalizedSlugFieldTestCase(TestCase): | |||||||
|     def test_formfield_unique(cls): |     def test_formfield_unique(cls): | ||||||
|         cls._test_formfield(LocalizedUniqueSlugField) |         cls._test_formfield(LocalizedUniqueSlugField) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_populate_multiple_from_fields_auto(cls): | ||||||
|  |         cls._test_populate_multiple_from_fields(LocalizedAutoSlugField) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_populate_multiple_from_fields_unique(cls): | ||||||
|  |         cls._test_populate_multiple_from_fields(LocalizedUniqueSlugField) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_populate_multiple_from_fields_fk_auto(cls): | ||||||
|  |         cls._test_populate_multiple_from_fields_fk(LocalizedAutoSlugField) | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def test_populate_multiple_from_fields_fk_unique(cls): | ||||||
|  |         cls._test_populate_multiple_from_fields_fk(LocalizedUniqueSlugField) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _test_populate(model): |     def _test_populate(model): | ||||||
|         """Tests whether the populating feature works correctly.""" |         """Tests whether the populating feature works correctly.""" | ||||||
| @@ -165,6 +184,67 @@ class LocalizedSlugFieldTestCase(TestCase): | |||||||
|  |  | ||||||
|         assert obj.slug.get('en') == slugify(obj.title) |         assert obj.slug.get('en') == slugify(obj.title) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def _test_populate_multiple_from_fields(field_type): | ||||||
|  |         """Tests whether populating the slug from multiple | ||||||
|  |         fields works correctly.""" | ||||||
|  |  | ||||||
|  |         model = get_fake_model( | ||||||
|  |             '_test_populate_multiple_from_fields_' + str(field_type), | ||||||
|  |             { | ||||||
|  |                 'title': LocalizedField(), | ||||||
|  |                 'name': models.CharField(max_length=255), | ||||||
|  |                 'slug': field_type(populate_from=('title', 'name')) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         obj = model() | ||||||
|  |         for lang_code, lang_name in settings.LANGUAGES: | ||||||
|  |             obj.name = 'swen' | ||||||
|  |             obj.title.set(lang_code, 'title %s' % lang_name) | ||||||
|  |  | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         for lang_code, lang_name in settings.LANGUAGES: | ||||||
|  |             assert obj.slug.get(lang_code) == 'title-%s-swen' % lang_name.lower() | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def _test_populate_multiple_from_fields_fk(field_type): | ||||||
|  |         """Tests whether populating the slug from multiple | ||||||
|  |         fields works correctly.""" | ||||||
|  |  | ||||||
|  |         model_fk = get_fake_model( | ||||||
|  |             '_test_populate_multiple_from_fields_fk_other_' + str(field_type), | ||||||
|  |             { | ||||||
|  |                 'name': LocalizedField(), | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         model = get_fake_model( | ||||||
|  |             '_test_populate_multiple_from_fields_fk_' + str(field_type), | ||||||
|  |             { | ||||||
|  |                 'title': LocalizedField(), | ||||||
|  |                 'other': models.ForeignKey(model_fk), | ||||||
|  |                 'slug': field_type(populate_from=('title', 'other.name')) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         other = model_fk.objects.create(name={settings.LANGUAGE_CODE: 'swen'}) | ||||||
|  |         # for lang_code, lang_name in settings.LANGUAGES: | ||||||
|  |         #     other.name.set(lang_code, 'swen') | ||||||
|  |  | ||||||
|  |         # other.save() | ||||||
|  |  | ||||||
|  |         obj = model() | ||||||
|  |         for lang_code, lang_name in settings.LANGUAGES: | ||||||
|  |             obj.other_id = other.id | ||||||
|  |             obj.title.set(lang_code, 'title %s' % lang_name) | ||||||
|  |  | ||||||
|  |         obj.save() | ||||||
|  |  | ||||||
|  |         for lang_code, lang_name in settings.LANGUAGES: | ||||||
|  |             assert obj.slug.get(lang_code) == 'title-%s-swen' % lang_name.lower() | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _test_populate_multiple_languages(model): |     def _test_populate_multiple_languages(model): | ||||||
|         """Tests whether the populating feature correctly |         """Tests whether the populating feature correctly | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user