Support for slugging from multiple fields

This commit is contained in:
Swen Kooij 2017-06-26 12:34:50 +03:00
parent 3b28a5e707
commit 51fc6959d2
3 changed files with 121 additions and 4 deletions

View File

@ -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)

View File

@ -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

View File

@ -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