Add TranslationHybrid

This commit is contained in:
Konsta Vesterinen
2014-11-26 13:30:55 +02:00
parent 0d2efa1015
commit 5e00019be5
3 changed files with 128 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ from .functions import (
sort_query,
table_name,
)
from .i18n import TranslationHybrid
from .listeners import (
auto_delete_orphans,
coercion_listener,

View File

@@ -1,3 +1,5 @@
from sqlalchemy.ext.hybrid import hybrid_property
from .exceptions import ImproperlyConfigured
@@ -21,3 +23,60 @@ except ImportError:
'install babel or make a similar function and override it '
'in this module.'
)
class TranslationHybrid(object):
def __init__(self, current_locale, default_locale):
self.current_locale = current_locale
self.default_locale = default_locale
def cast_locale(self, obj, locale):
"""
Cast given locale to string. Supports also callbacks that return
locales.
"""
if callable(locale):
try:
return str(locale())
except TypeError:
return str(locale(obj))
return str(locale)
def getter_factory(self, attr):
"""
Return a hybrid_property getter function for given attribute. The
returned getter first checks if object has translation for current
locale. If not it tries to get translation for default locale. If there
is no translation found for default locale it returns None.
"""
def getter(obj):
current_locale = self.cast_locale(obj, self.current_locale)
try:
return getattr(obj, attr.key)[current_locale]
except (TypeError, KeyError):
default_locale = self.cast_locale(
obj, self.default_locale
)
try:
return getattr(obj, attr.key)[default_locale]
except (TypeError, KeyError):
return None
return getter
def setter_factory(self, attr):
def setter(obj, value):
if getattr(obj, attr.key) is None:
setattr(obj, attr.key, {})
locale = self.cast_locale(obj, self.current_locale)
getattr(obj, attr.key)[locale] = value
return setter
def expr_factory(self, attr):
return lambda cls: attr
def __call__(self, attr):
return hybrid_property(
fget=self.getter_factory(attr),
fset=self.setter_factory(attr),
expr=self.expr_factory(attr)
)

View File

@@ -0,0 +1,68 @@
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy_utils import TranslationHybrid
from tests import TestCase
class TestTranslationHybrid(TestCase):
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
def create_models(self):
class City(self.Base):
__tablename__ = 'city'
id = sa.Column(sa.Integer, primary_key=True)
name_translations = sa.Column(JSON())
name = self.translation_hybrid(name_translations)
locale = 'en'
self.City = City
def setup_method(self, method):
self.translation_hybrid = TranslationHybrid('fi', 'en')
TestCase.setup_method(self, method)
def test_using_hybrid_as_constructor(self):
city = self.City(name='Helsinki')
assert city.name_translations['fi'] == 'Helsinki'
def test_hybrid_as_expression(self):
assert self.City.name == self.City.name_translations
def test_if_no_translation_exists_returns_none(self):
city = self.City()
assert city.name is None
def test_fall_back_to_default_translation(self):
city = self.City(name_translations={'en': 'Helsinki'})
self.translation_hybrid.current_locale = 'sv'
assert city.name == 'Helsinki'
class TestTranslationHybridWithDynamicDefaultLocale(TestCase):
dns = 'postgres://postgres@localhost/sqlalchemy_utils_test'
def create_models(self):
class City(self.Base):
__tablename__ = 'city'
id = sa.Column(sa.Integer, primary_key=True)
name_translations = sa.Column(JSON)
name = self.translation_hybrid(name_translations)
locale = sa.Column(sa.String(10))
self.City = City
def setup_method(self, method):
self.translation_hybrid = TranslationHybrid(
'fi',
lambda self: self.locale
)
TestCase.setup_method(self, method)
def test_fallback_to_dynamic_locale(self):
self.translation_hybrid.current_locale = 'en'
city = self.City(name_translations={})
city.locale = 'fi'
city.name_translations['fi'] = 'Helsinki'
assert city.name == 'Helsinki'