Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = 'localized_fields.apps.LocalizedFieldsConfig'
16 changes: 16 additions & 0 deletions localized_fields/apps.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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()