0
0
mirror of https://github.com/django/django.git synced 2024-11-24 20:07:01 +01:00

Demonstrate how implicit ordering of extra(select) can be preserved.

This commit is contained in:
Simon Charette 2024-09-02 23:51:49 -04:00
parent 9a83c66561
commit ed6788bbd7
No known key found for this signature in database
4 changed files with 53 additions and 9 deletions

View File

@ -210,9 +210,19 @@ class ValuesIterable(BaseIterable):
if query.selected:
names = list(query.selected)
else:
# RemovedInDjango60Warning: place extra(select) entries at the
# beginning of the SELECT clause until it's completely removed.
extra_names = []
annotation_select = []
for alias, expression in query.annotation_select.items():
if isinstance(expression, _ExtraSelectFirstRawSQL):
extra_names.append(alias)
else:
annotation_select.append(alias)
names = [
*extra_names,
*query.values_select,
*query.annotation_select,
*annotation_select,
]
indexes = range(len(names))
for row in compiler.results_iter(
@ -250,9 +260,19 @@ class NamedValuesListIterable(ValuesListIterable):
names = queryset._fields
else:
query = queryset.query
# RemovedInDjango60Warning: place extra(select) entries at the
# beginning of the SELECT clause until it's completely removed.
extra_names = []
annotation_select = []
for alias, expression in query.annotation_select.items():
if isinstance(expression, _ExtraSelectFirstRawSQL):
extra_names.append(alias)
else:
annotation_select.append(alias)
names = [
*extra_names,
*query.values_select,
*query.annotation_select,
*annotation_select,
]
tuple_class = create_namedtuple_class(*names)
new = tuple.__new__
@ -275,6 +295,17 @@ class FlatValuesListIterable(BaseIterable):
yield row[0]
# RemovedInDjango60Warning
class _ExtraSelectFirstRawSQL(RawSQL):
"""
Internal RawSQL subclass used during the deprecation of extra(select) to
preserve the undocumented implicit ordering of members at the begining of
the SELECT clause.
"""
implicitly_select_first = True
class QuerySet(AltersData):
"""Represent a lazy database lookup for a set of objects."""
@ -1724,8 +1755,6 @@ class QuerySet(AltersData):
category=RemovedInDjango60Warning,
stacklevel=2,
)
# XXX: This is mising the logic to always place extras at the begining
# of the select clause.
if select_params:
param_iter = iter(select_params)
else:
@ -1739,7 +1768,9 @@ class QuerySet(AltersData):
if pos == 0 or entry[pos - 1] != "%":
entry_params.append(next(param_iter))
pos = entry.find("%s", pos + 2)
clone.query.add_annotation(RawSQL(entry, entry_params), name)
clone.query.add_annotation(
_ExtraSelectFirstRawSQL(entry, entry_params), name
)
if where:
warnings.warn(
"extra(where) usage is deprecated, use filter() with RawSQL instead.",

View File

@ -262,9 +262,22 @@ class SQLCompiler:
}
selected = []
if self.query.selected is None:
# RemovedInDjango60Warning: place extra(select) entries at the
# beginning of the SELECT clause until it's completely removed.
leading_annotation_select = []
annotation_select = []
if self.query.values_select_all:
for alias, expression in self.query.annotation_select.items():
if getattr(expression, "implicitly_select_first", False):
leading_annotation_select.append((alias, expression))
else:
annotation_select.append((alias, expression))
else:
annotation_select = self.query.annotation_select.items()
selected = [
*leading_annotation_select,
*((None, col) for col in cols),
*self.query.annotation_select.items(),
*annotation_select,
]
else:
for alias, expression in self.query.selected.items():

View File

@ -286,7 +286,7 @@ class ExtraRegressTests(TestCase):
select={"foo": "first", "bar": "second", "whiz": "third"}
).values_list()
),
[(obj.pk, "first", "second", "third", "first", "second", "third")],
[("first", "second", "third", obj.pk, "first", "second", "third")],
)
# Extra columns after an empty values_list() are still included
@ -296,7 +296,7 @@ class ExtraRegressTests(TestCase):
select={"foo": "first", "bar": "second", "whiz": "third"}
)
),
[(obj.pk, "first", "second", "third", "first", "second", "third")],
[("first", "second", "third", obj.pk, "first", "second", "third")],
)
# Extra columns ignored completely if not mentioned in values_list()

View File

@ -2841,7 +2841,7 @@ class ValuesQuerysetTests(TestCase):
self.assertEqual(type(values).__name__, "Row")
self.assertEqual(
values._fields,
("id", "num", "other_num", "another_num", "num2", "id__count"),
("num2", "id", "num", "other_num", "another_num", "id__count"),
)
self.assertEqual(values.num, 72)
self.assertEqual(values.num2, 73)