mirror of
https://github.com/SectorLabs/django-localized-fields.git
synced 2025-04-24 19:32:53 +03:00
Add support for localized query look ups
This commit is contained in:
parent
88e2d29596
commit
ff836836bf
@ -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
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
default_app_config = 'localized_fields.apps.LocalizedFieldsConfig'
|
@ -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
localized_fields/lookups.py
Normal file
80
localized_fields/lookups.py
Normal 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
51
tests/test_lookups.py
Normal 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()
|
Loading…
x
Reference in New Issue
Block a user