diff options
| author | Simon Charette <charette.s@gmail.com> | 2025-04-02 18:53:36 -0400 |
|---|---|---|
| committer | Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> | 2025-04-11 09:06:53 +0200 |
| commit | b97af5e696b235876c1a1c8d8a704580f2aeabc2 (patch) | |
| tree | 8b70ed9a518b3f915e0dab759b91d3ed959abe67 /django | |
| parent | 0ba34e7ca0098db26344b7bb5b07061999ae09d4 (diff) | |
[5.2.x] Fixed #36288 -- Addressed improper handling of duplicates in values_list().
Now that selected aliases are stored in sql.Query.selected: dict[str, Any]
the values_list() method must ensures that duplicate field name references are
assigned unique aliases.
Refs #28900.
Regression in 65ad4ade74dc9208b9d686a451cd6045df0c9c3a.
Thanks Claude for the report.
Backport of 21f8be76d43aa1ee5ae41c1e0a428cfea1f231c1 from main.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/models/query.py | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py index b898f3ec1a..46733c8cb0 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1375,24 +1375,33 @@ class QuerySet(AltersData): "field." ) - field_names = {f for f in fields if not hasattr(f, "resolve_expression")} + field_names = {f: False for f in fields if not hasattr(f, "resolve_expression")} _fields = [] expressions = {} counter = 1 for field in fields: + field_name = field + expression = None if hasattr(field, "resolve_expression"): - field_id_prefix = getattr( + field_name = getattr( field, "default_alias", field.__class__.__name__.lower() ) - while True: - field_id = field_id_prefix + str(counter) + expression = field + # For backward compatibility reasons expressions are always + # prefixed with the counter even if their default alias doesn't + # collide with field names. Changing this logic could break + # some usage of named=True. + seen = True + elif seen := field_names[field_name]: + expression = F(field_name) + if seen: + field_name_prefix = field_name + while (field_name := f"{field_name_prefix}{counter}") in field_names: counter += 1 - if field_id not in field_names: - break - expressions[field_id] = field - _fields.append(field_id) - else: - _fields.append(field) + if expression is not None: + expressions[field_name] = expression + field_names[field_name] = True + _fields.append(field_name) clone = self._values(*_fields, **expressions) clone._iterable_class = ( |
