Fixed #34437 -- Made values() resolving error mention selected annotations.

While the add_fields() call from set_values() does trigger validation it
does so after annotations are masked resulting in them being excluded
from the choices of valid options surfaced through a FieldError.
This commit is contained in:
Simon Charette 2023-03-25 15:22:45 -04:00 committed by GitHub
parent f5c5c571d3
commit cb13792938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 21 additions and 5 deletions

View File

@ -2135,11 +2135,6 @@ class Query(BaseExpression):
# For lookups spanning over relationships, show the error
# from the model on which the lookup failed.
raise
elif name in self.annotations:
raise FieldError(
"Cannot select the '%s' alias. Use annotate() to promote "
"it." % name
)
else:
names = sorted(
[
@ -2385,7 +2380,17 @@ class Query(BaseExpression):
extra_names.append(f)
elif f in self.annotation_select:
annotation_names.append(f)
elif f in self.annotations:
raise FieldError(
f"Cannot select the '{f}' alias. Use annotate() to "
"promote it."
)
else:
# Call `names_to_path` to ensure a FieldError including
# annotations about to be masked as valid choices if
# `f` is not resolvable.
if self.annotation_select:
self.names_to_path(f.split(LOOKUP_SEP), self.model._meta)
field_names.append(f)
self.set_extra_mask(extra_names)
self.set_annotation_mask(annotation_names)

View File

@ -33,6 +33,7 @@ from django.db.models.functions import (
Lower,
Trim,
)
from django.db.models.sql.query import get_field_names_from_opts
from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import register_lookup
@ -465,6 +466,16 @@ class NonAggregateAnnotationTestCase(TestCase):
)
)
def test_values_wrong_annotation(self):
expected_message = (
"Cannot resolve keyword 'annotation_typo' into field. Choices are: %s"
)
article_fields = ", ".join(
["annotation"] + sorted(get_field_names_from_opts(Book._meta))
)
with self.assertRaisesMessage(FieldError, expected_message % article_fields):
Book.objects.annotate(annotation=Value(1)).values_list("annotation_typo")
def test_decimal_annotation(self):
salary = Decimal(10) ** -Employee._meta.get_field("salary").decimal_places
Employee.objects.create(