Skip to content

Commit

Permalink
Add support for localized query look ups
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver Sauder committed Dec 3, 2018
1 parent 88e2d29 commit ff83683
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.rst
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions localized_fields/__init__.py
@@ -0,0 +1 @@
default_app_config = 'localized_fields.apps.LocalizedFieldsConfig'
16 changes: 16 additions & 0 deletions localized_fields/apps.py
@@ -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)
80 changes: 80 additions & 0 deletions localized_fields/lookups.py
@@ -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 changes: 51 additions & 0 deletions tests/test_lookups.py
@@ -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()

0 comments on commit ff83683

Please sign in to comment.