Compare commits

..

8 Commits

Author SHA1 Message Date
Swen Kooij
d9913f1eec
Bump version to 6.8b5 2024-07-02 07:45:57 +03:00
Swen Kooij
3b973361d3 Display pointer cursor when hovering tabs 2024-07-02 07:38:11 +03:00
seroy
4c5d45f25a Fix tabs displaying in Django 5.x 2024-07-02 07:38:11 +03:00
Swen Kooij
e6527fff4c 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'
```
2024-07-02 07:10:41 +03:00
Swen Kooij
25259b8469 Silence our own deprecation warning when running the tests 2024-07-02 07:10:29 +03:00
Swen Kooij
84beade9e0 Make Django tests use USE_TZ = True to silence Django 5.x deprecation warning 2024-07-02 07:10:29 +03:00
Swen Kooij
150f671115 Pin click version for Black 2024-07-02 07:10:29 +03:00
Roman Selivanov
c35844b471 Fix type hint for required kwarg 2024-07-02 07:10:29 +03:00
7 changed files with 65 additions and 13 deletions

View File

@ -28,18 +28,26 @@ class LocalizedField(HStoreField):
descriptor_class = LocalizedValueDescriptor
def __init__(
self, *args, required: Union[bool, List[str]] = None, **kwargs
self,
*args,
required: Optional[Union[bool, List[str]]] = None,
blank: bool = False,
**kwargs
):
"""Initializes a new instance of :see:LocalizedField."""
super(LocalizedField, self).__init__(*args, required=required, **kwargs)
if (self.required is None and self.blank) or self.required is False:
if (required is None and blank) or required is False:
self.required = []
elif self.required is None and not self.blank:
elif required is None and not blank:
self.required = [settings.LANGUAGE_CODE]
elif self.required is True:
elif required is True:
self.required = [lang_code for lang_code, _ in settings.LANGUAGES]
else:
self.required = required
super(LocalizedField, self).__init__(
*args, required=self.required, blank=blank, **kwargs
)
def contribute_to_class(self, model, name, **kwargs):
"""Adds this field to the specifed model.

View File

@ -23,6 +23,7 @@ from django.db.models.lookups import (
StartsWith,
)
from django.utils import translation
from psqlextra.expressions import HStoreColumn
from .fields import LocalizedField
@ -37,14 +38,33 @@ except ImportError:
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)
# If the LHS is already a reference to a specific hstore key, there
# is nothing to be done since it already references as specific language.
if isinstance(self.lhs, HStoreColumn) or isinstance(
self.lhs, KeyTransform
):
return super().process_lhs(qn, connection)
# If this is something custom expression, we don't really know how to
# handle that, so we better do nothing.
if not isinstance(self.lhs, Col):
return super().process_lhs(qn, connection)
# Select the key for the current language. We do this so that
#
# myfield__<lookup>=
#
# Is converted into:
#
# myfield__<lookup>__<current language>=
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):
# Django 4.0 removed the ability for isnull fields to be something other than a bool
# We should NOT convert them to strings
# Django 4.0 removed the ability for isnull fields to be something
# other than a bool We should NOT convert them to strings
if isinstance(self.rhs, bool):
return self.rhs
return str(self.rhs)

View File

@ -34,10 +34,11 @@
.localized-fields-widget.tabs .localized-fields-widget.tab label {
padding: 5px 10px;
display: inline-block;
display: inline;
text-decoration: none;
color: #fff;
width: initial;
cursor: pointer;
}
.localized-fields-widget.tabs .localized-fields-widget.tab.active,

View File

@ -3,3 +3,5 @@ DJANGO_SETTINGS_MODULE=settings
testpaths=tests
addopts=-m "not benchmark"
junit_family=legacy
filterwarnings=
ignore::DeprecationWarning:localized_fields.fields.autoslug_field

View File

@ -1,3 +1,4 @@
import django
import dj_database_url
DEBUG = True
@ -51,6 +52,12 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
# See: https://github.com/psycopg/psycopg2/issues/1293
if django.VERSION >= (3, 1):
USE_TZ = True
USE_I18N = True
TIME_ZONE = 'UTC'
# set to a lower number than the default, since
# we want the tests to be fast, default is 100
LOCALIZED_FIELDS_MAX_RETRIES = 3

View File

@ -36,7 +36,7 @@ with open(
setup(
name="django-localized-fields",
version="6.8b4",
version="6.8b5",
packages=find_packages(exclude=["tests"]),
include_package_data=True,
license="MIT License",
@ -98,6 +98,7 @@ setup(
"autopep8==1.4.4",
"isort==4.3.20",
"sl-docformatter==1.4",
"click==8.0.2",
],
},
cmdclass={

View File

@ -3,6 +3,7 @@ from django.conf import settings
from django.test import TestCase, override_settings
from django.utils import translation
from localized_fields.expressions import LocalizedRef
from localized_fields.fields import LocalizedField
from localized_fields.value import LocalizedValue
@ -49,6 +50,18 @@ class LocalizedLookupsTestCase(TestCase):
# ensure that hstore lookups still work
assert self.TestModel.objects.filter(text__ro="text_ro").exists()
def test_localized_lookup_specific_isnull(self):
self.TestModel.objects.create(
text=LocalizedValue(dict(en="text_en", ro="text_ro", nl=None))
)
translation.activate("nl")
assert (
self.TestModel.objects.annotate(text_localized=LocalizedRef("text"))
.filter(text_localized__isnull=True)
.exists()
)
class LocalizedRefLookupsTestCase(TestCase):
"""Tests whether ref lookups properly work with."""