Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for localized query look ups
- Loading branch information
Oliver Sauder
committed
Dec 3, 2018
1 parent
88e2d29
commit ff83683
Showing
5 changed files
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
default_app_config = 'localized_fields.apps.LocalizedFieldsConfig' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |