Skip to content

Commit

Permalink
Merge pull request #671 from th3hamm0r/fix/queryset-values-with-expre…
Browse files Browse the repository at this point in the history
…ssions

fix: Fix handling of expressions in `values()`/`values_list()`
  • Loading branch information
last-partizan committed Feb 9, 2023
2 parents 943e902 + d65ff60 commit 3c0735c
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 4 deletions.
25 changes: 22 additions & 3 deletions modeltranslation/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,8 @@ def only(self, *fields):
return super(MultilingualQuerySet, self).only(*fields)

# This method was not present in django-linguo
def raw_values(self, *fields):
return super(MultilingualQuerySet, self).values(*fields)
def raw_values(self, *fields, **expressions):
return super(MultilingualQuerySet, self).values(*fields, **expressions)

def _values(self, *original, **kwargs):
selects_all = kwargs.pop('selects_all', False)
Expand All @@ -429,6 +429,7 @@ def values(self, *fields, **expressions):
if not fields:
# Emulate original queryset behaviour: get all fields that are not translation fields
fields = self._get_original_fields()
fields += tuple(expressions)
clone = self._values(*fields, prepare=True, selects_all=selects_all, **expressions)
clone._iterable_class = FallbackValuesIterable
return clone
Expand All @@ -447,7 +448,25 @@ def values_list(self, *fields, flat=False, named=False):
if not fields:
# Emulate original queryset behaviour: get all fields that are not translation fields
fields = self._get_original_fields()
clone = self._values(*fields, prepare=True, selects_all=selects_all)

field_names = {f for f in fields if not hasattr(f, 'resolve_expression')}
_fields = []
expressions = {}
counter = 1
for field in fields:
if hasattr(field, 'resolve_expression'):
field_id_prefix = getattr(field, 'default_alias', field.__class__.__name__.lower())
while True:
field_id = field_id_prefix + str(counter)
counter += 1
if field_id not in field_names:
break
expressions[field_id] = field
_fields.append(field_id)
else:
_fields.append(field)

clone = self._values(*_fields, prepare=True, selects_all=selects_all, **expressions)
clone._iterable_class = (
FallbackNamedValuesListIterable
if named
Expand Down
26 changes: 25 additions & 1 deletion modeltranslation/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
from django.core.management import call_command
from django.core.management.base import CommandError
from django.db import IntegrityError
from django.db.models import Count, F, Q, TextField, Value
from django.db.models import CharField, Count, F, Q, TextField, Value
from django.db.models.functions import Cast
from django.test import TestCase, TransactionTestCase
from django.test.utils import override_settings
from django.utils.translation import get_language, override, trans_real
Expand Down Expand Up @@ -2993,6 +2994,29 @@ def test_values_list_annotation(self):
('foo', 2)
]

def test_values_with_expressions(self):
manager = models.ManagerTestModel.objects
id1 = manager.create(title_en='en', title_de='de').pk

raw_obj = manager.raw_values('title', str_pk=Cast("pk", output_field=CharField()))[0]
obj = manager.values('title', str_pk=Cast("pk", output_field=CharField()))[0]
with override('de'):
raw_obj2 = manager.raw_values('title', str_pk=Cast("pk", output_field=CharField()))[0]
obj2 = manager.values('title', str_pk=Cast("pk", output_field=CharField()))[0]

# Raw_values returns real database values regardless of current language
assert raw_obj['title'] == raw_obj2['title']
assert raw_obj['str_pk'] == raw_obj2['str_pk']
# Values present language-aware data, from the moment of retrieval
assert obj['title'] == 'en'
assert obj['str_pk'] == str(id1)
assert obj2['title'] == 'de'

# Values_list behave similarly
assert list(manager.values_list('title', Cast("pk", output_field=CharField()))) == [('en', str(id1))]
with override('de'):
assert list(manager.values_list('title', Cast("pk", output_field=CharField()))) == [('de', str(id1))]

def test_custom_manager(self):
"""Test if user-defined manager is still working"""
n = models.CustomManagerTestModel(title='')
Expand Down

0 comments on commit 3c0735c

Please sign in to comment.