Add support for localized query look ups

This commit is contained in:
Oliver Sauder 2018-08-27 11:52:30 +02:00
parent 88e2d29596
commit ff836836bf
5 changed files with 149 additions and 0 deletions

View File

@ -348,6 +348,7 @@ Experimental feature
^^^^^^^^^^^^^^^^^^^^
Enables the following experimental features:
* ``LocalizedField`` will return ``None`` instead of an empty ``LocalizedValue`` if there is no database value.
* ``LocalizedField`` lookups will lookup by currently active language instead of HStoreField
.. code-block:: python

View File

@ -0,0 +1 @@
default_app_config = 'localized_fields.apps.LocalizedFieldsConfig'

View File

@ -1,5 +1,21 @@
import inspect
from django.apps import AppConfig
from django.conf import settings
from . import lookups
from .fields import LocalizedField
from .lookups import LocalizedLookupMixin
class LocalizedFieldsConfig(AppConfig):
name = 'localized_fields'
def ready(self):
if getattr(settings, 'LOCALIZED_FIELDS_EXPERIMENTAL', False):
for _, clazz in inspect.getmembers(lookups):
if not inspect.isclass(clazz) or clazz is LocalizedLookupMixin:
continue
if issubclass(clazz, LocalizedLookupMixin):
LocalizedField.register_lookup(clazz)

View File

@ -0,0 +1,80 @@
from django.conf import settings
from django.contrib.postgres.fields.hstore import KeyTransform
from django.contrib.postgres.lookups import (SearchLookup, TrigramSimilar,
Unaccent)
from django.db.models.expressions import Col
from django.db.models.lookups import (Contains, EndsWith, Exact, IContains,
IEndsWith, IExact, In, IRegex, IsNull,
IStartsWith, Regex, StartsWith)
from django.utils import translation
class LocalizedLookupMixin():
def process_lhs(self, qn, connection):
if isinstance(self.lhs, Col):
language = translation.get_language() or settings.LANGUAGE_CODE
self.lhs = KeyTransform(language, self.lhs)
return super().process_lhs(qn, connection)
def get_prep_lookup(self):
return str(self.rhs)
class LocalizedSearchLookup(LocalizedLookupMixin, SearchLookup):
pass
class LocalizedUnaccent(LocalizedLookupMixin, Unaccent):
pass
class LocalizedTrigramSimilair(LocalizedLookupMixin, TrigramSimilar):
pass
class LocalizedExact(LocalizedLookupMixin, Exact):
pass
class LocalizedIExact(LocalizedLookupMixin, IExact):
pass
class LocalizedIn(LocalizedLookupMixin, In):
pass
class LocalizedContains(LocalizedLookupMixin, Contains):
pass
class LocalizedIContains(LocalizedLookupMixin, IContains):
pass
class LocalizedStartsWith(LocalizedLookupMixin, StartsWith):
pass
class LocalizedIStartsWith(LocalizedLookupMixin, IStartsWith):
pass
class LocalizedEndsWith(LocalizedLookupMixin, EndsWith):
pass
class LocalizedIEndsWith(LocalizedLookupMixin, IEndsWith):
pass
class LocalizedIsNullWith(LocalizedLookupMixin, IsNull):
pass
class LocalizedRegexWith(LocalizedLookupMixin, Regex):
pass
class LocalizedIRegexWith(LocalizedLookupMixin, IRegex):
pass

51
tests/test_lookups.py Normal file
View File

@ -0,0 +1,51 @@
from django.apps import apps
from django.conf import settings
from django.test import TestCase, override_settings
from django.utils import translation
from localized_fields.fields import LocalizedField
from localized_fields.value import LocalizedValue
from .fake_model import get_fake_model
@override_settings(LOCALIZED_FIELDS_EXPERIMENTAL=True)
class LocalizedLookupsTestCase(TestCase):
"""Tests whether localized lookups properly work with."""
TestModel1 = None
@classmethod
def setUpClass(cls):
"""Creates the test model in the database."""
super(LocalizedLookupsTestCase, cls).setUpClass()
# reload app as setting has changed
config = apps.get_app_config('localized_fields')
config.ready()
cls.TestModel = get_fake_model(
{
'text': LocalizedField(),
}
)
def test_localized_lookup(self):
"""Tests whether localized lookup properly works."""
self.TestModel.objects.create(
text=LocalizedValue(dict(en='text_en', ro='text_ro', nl='text_nl')),
)
# assert that it properly lookups the currently active language
for lang_code, _ in settings.LANGUAGES:
translation.activate(lang_code)
assert self.TestModel.objects.filter(text='text_' + lang_code).exists()
# ensure that the default language is used in case no
# language is active at all
translation.deactivate_all()
assert self.TestModel.objects.filter(text='text_en').exists()
# ensure that hstore lookups still work
assert self.TestModel.objects.filter(text__ro='text_ro').exists()