Skip to content

Commit 153f866

Browse files
committed
Don't double key transform in lookups
In Django 4.2, `process_lhs()` is called for IsNull lookups, which is how this bug came to appear. It was already there and could be reproduced on Django 3.2. The issue is when `LocalizedRef` is combined with a localized lookup. The lookup is unaware of the existing localized ref and adds another transform on top. This results in the expression becoming: ``` field->'en'->'en' ```
1 parent 5bb16af commit 153f866

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

localized_fields/lookups.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
StartsWith,
2424
)
2525
from django.utils import translation
26+
from psqlextra.expressions import HStoreColumn
2627

2728
from .fields import LocalizedField
2829

@@ -37,14 +38,33 @@ class NullIf(Func):
3738

3839
class LocalizedLookupMixin:
3940
def process_lhs(self, qn, connection):
40-
if isinstance(self.lhs, Col):
41-
language = translation.get_language() or settings.LANGUAGE_CODE
42-
self.lhs = KeyTransform(language, self.lhs)
41+
# If the LHS is already a reference to a specific hstore key, there
42+
# is nothing to be done since it already references as specific language.
43+
if isinstance(self.lhs, HStoreColumn) or isinstance(
44+
self.lhs, KeyTransform
45+
):
46+
return super().process_lhs(qn, connection)
47+
48+
# If this is something custom expression, we don't really know how to
49+
# handle that, so we better do nothing.
50+
if not isinstance(self.lhs, Col):
51+
return super().process_lhs(qn, connection)
52+
53+
# Select the key for the current language. We do this so that
54+
#
55+
# myfield__<lookup>=
56+
#
57+
# Is converted into:
58+
#
59+
# myfield__<lookup>__<current language>=
60+
language = translation.get_language() or settings.LANGUAGE_CODE
61+
self.lhs = KeyTransform(language, self.lhs)
62+
4363
return super().process_lhs(qn, connection)
4464

4565
def get_prep_lookup(self):
46-
# Django 4.0 removed the ability for isnull fields to be something other than a bool
47-
# We should NOT convert them to strings
66+
# Django 4.0 removed the ability for isnull fields to be something
67+
# other than a bool We should NOT convert them to strings
4868
if isinstance(self.rhs, bool):
4969
return self.rhs
5070
return str(self.rhs)

tests/test_lookups.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.test import TestCase, override_settings
44
from django.utils import translation
55

6+
from localized_fields.expressions import LocalizedRef
67
from localized_fields.fields import LocalizedField
78
from localized_fields.value import LocalizedValue
89

@@ -49,6 +50,18 @@ def test_localized_lookup(self):
4950
# ensure that hstore lookups still work
5051
assert self.TestModel.objects.filter(text__ro="text_ro").exists()
5152

53+
def test_localized_lookup_specific_isnull(self):
54+
self.TestModel.objects.create(
55+
text=LocalizedValue(dict(en="text_en", ro="text_ro", nl=None))
56+
)
57+
58+
translation.activate("nl")
59+
assert (
60+
self.TestModel.objects.annotate(text_localized=LocalizedRef("text"))
61+
.filter(text_localized__isnull=True)
62+
.exists()
63+
)
64+
5265

5366
class LocalizedRefLookupsTestCase(TestCase):
5467
"""Tests whether ref lookups properly work with."""

0 commit comments

Comments
 (0)