mirror of
				https://github.com/SectorLabs/django-localized-fields.git
				synced 2025-11-04 12:08:57 +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 django import forms
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.utils import translation
 | 
			
		||||
from django.utils.text import slugify
 | 
			
		||||
 | 
			
		||||
from .field import LocalizedField
 | 
			
		||||
from ..value import LocalizedValue
 | 
			
		||||
from ..util import resolve_object_property
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LocalizedAutoSlugField(LocalizedField):
 | 
			
		||||
@@ -147,7 +149,7 @@ class LocalizedAutoSlugField(LocalizedField):
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    @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.
 | 
			
		||||
 | 
			
		||||
        Arguments:
 | 
			
		||||
@@ -164,5 +166,17 @@ class LocalizedAutoSlugField(LocalizedField):
 | 
			
		||||
            The text to generate a slug for.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        value = getattr(instance, field_name, None)
 | 
			
		||||
        return value.get(language)
 | 
			
		||||
        def get_field_value(name):
 | 
			
		||||
            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
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from django.db.utils import IntegrityError
 | 
			
		||||
@@ -31,6 +32,7 @@ class LocalizedSlugFieldTestCase(TestCase):
 | 
			
		||||
            'LocalizedAutoSlugFieldTestModel',
 | 
			
		||||
            {
 | 
			
		||||
                'title': LocalizedField(),
 | 
			
		||||
                'name': models.CharField(max_length=255),
 | 
			
		||||
                'slug': LocalizedAutoSlugField(populate_from='title')
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
@@ -39,6 +41,7 @@ class LocalizedSlugFieldTestCase(TestCase):
 | 
			
		||||
            'LocalizedUniqueSlugFieldTestModel',
 | 
			
		||||
            {
 | 
			
		||||
                'title': LocalizedField(),
 | 
			
		||||
                'name': models.CharField(max_length=255),
 | 
			
		||||
                'slug': LocalizedUniqueSlugField(populate_from='title')
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
@@ -155,6 +158,22 @@ class LocalizedSlugFieldTestCase(TestCase):
 | 
			
		||||
    def test_formfield_unique(cls):
 | 
			
		||||
        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
 | 
			
		||||
    def _test_populate(model):
 | 
			
		||||
        """Tests whether the populating feature works correctly."""
 | 
			
		||||
@@ -165,6 +184,67 @@ class LocalizedSlugFieldTestCase(TestCase):
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
    def _test_populate_multiple_languages(model):
 | 
			
		||||
        """Tests whether the populating feature correctly
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user