mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #27719 -- Added QuerySet.alias() to allow creating reusable aliases.
QuerySet.alias() allows creating reusable aliases for expressions that don't need to be selected but are used for filtering, ordering, or as a part of complex expressions. Thanks Simon Charette for reviews.
This commit is contained in:
committed by
Mariusz Felisiak
parent
88af11c58b
commit
f4ac167119
@@ -1085,6 +1085,16 @@ class QuerySet:
|
||||
with extra data or aggregations.
|
||||
"""
|
||||
self._not_support_combined_queries('annotate')
|
||||
return self._annotate(args, kwargs, select=True)
|
||||
|
||||
def alias(self, *args, **kwargs):
|
||||
"""
|
||||
Return a query set with added aliases for extra data or aggregations.
|
||||
"""
|
||||
self._not_support_combined_queries('alias')
|
||||
return self._annotate(args, kwargs, select=False)
|
||||
|
||||
def _annotate(self, args, kwargs, select=True):
|
||||
self._validate_values_are_expressions(args + tuple(kwargs.values()), method_name='annotate')
|
||||
annotations = {}
|
||||
for arg in args:
|
||||
@@ -1114,8 +1124,9 @@ class QuerySet:
|
||||
if isinstance(annotation, FilteredRelation):
|
||||
clone.query.add_filtered_relation(annotation, alias)
|
||||
else:
|
||||
clone.query.add_annotation(annotation, alias, is_summary=False)
|
||||
|
||||
clone.query.add_annotation(
|
||||
annotation, alias, is_summary=False, select=select,
|
||||
)
|
||||
for alias, annotation in clone.query.annotations.items():
|
||||
if alias in annotations and annotation.contains_aggregate:
|
||||
if clone._fields is None:
|
||||
|
||||
@@ -1015,11 +1015,14 @@ class Query(BaseExpression):
|
||||
alias = seen[int_model] = join_info.joins[-1]
|
||||
return alias or seen[None]
|
||||
|
||||
def add_annotation(self, annotation, alias, is_summary=False):
|
||||
def add_annotation(self, annotation, alias, is_summary=False, select=True):
|
||||
"""Add a single annotation expression to the Query."""
|
||||
annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None,
|
||||
summarize=is_summary)
|
||||
self.append_annotation_mask([alias])
|
||||
if select:
|
||||
self.append_annotation_mask([alias])
|
||||
else:
|
||||
self.set_annotation_mask(set(self.annotation_select).difference({alias}))
|
||||
self.annotations[alias] = annotation
|
||||
|
||||
def resolve_expression(self, query, *args, **kwargs):
|
||||
@@ -1707,6 +1710,11 @@ class Query(BaseExpression):
|
||||
# which is executed as a wrapped subquery if any of the
|
||||
# aggregate() elements reference an existing annotation. In
|
||||
# that case we need to return a Ref to the subquery's annotation.
|
||||
if name not in self.annotation_select:
|
||||
raise FieldError(
|
||||
"Cannot aggregate over the '%s' alias. Use annotate() "
|
||||
"to promote it." % name
|
||||
)
|
||||
return Ref(name, self.annotation_select[name])
|
||||
else:
|
||||
return annotation
|
||||
@@ -1911,6 +1919,11 @@ 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([
|
||||
*get_field_names_from_opts(opts), *self.extra,
|
||||
|
||||
Reference in New Issue
Block a user