@@ -313,6 +313,41 @@ def relabeled_clone(self, change_map):
313
313
clone .change_aliases (change_map )
314
314
return clone
315
315
316
+ def rewrite_cols (self , annotation , col_cnt ):
317
+ # We must make sure the inner query has the referred columns in it.
318
+ # If we are aggregating over an annotation, then Django uses Ref()
319
+ # instances to note this. However, if we are annotating over a column
320
+ # of a related model, then it might be that column isn't part of the
321
+ # SELECT clause of the inner query, and we must manually make sure
322
+ # the column is selected. An example case is:
323
+ # .aggregate(Sum('author__awards'))
324
+ # Resolving this expression results in a join to author, but there
325
+ # is no guarantee the awards column of author is in the select clause
326
+ # of the query. Thus we must manually add the column to the inner
327
+ # query.
328
+ orig_exprs = annotation .get_source_expressions ()
329
+ new_exprs = []
330
+ for expr in orig_exprs :
331
+ if isinstance (expr , Ref ):
332
+ # Its already a Ref to subquery (see resolve_ref() for
333
+ # details)
334
+ new_exprs .append (expr )
335
+ elif isinstance (expr , Col ):
336
+ # Reference to column. Make sure the referenced column
337
+ # is selected.
338
+ col_cnt += 1
339
+ col_alias = '__col%d' % col_cnt
340
+ self .annotation_select [col_alias ] = expr
341
+ self .append_annotation_mask ([col_alias ])
342
+ new_exprs .append (Ref (col_alias , expr ))
343
+ else :
344
+ # Some other expression not referencing database values
345
+ # directly. Its subexpression might contain Cols.
346
+ new_expr , col_cnt = self .rewrite_cols (expr , col_cnt )
347
+ new_exprs .append (new_expr )
348
+ annotation .set_source_expressions (new_exprs )
349
+ return annotation , col_cnt
350
+
316
351
def get_aggregation (self , using , added_aggregate_names ):
317
352
"""
318
353
Returns the dictionary with the values of the existing aggregations.
@@ -350,11 +385,11 @@ def get_aggregation(self, using, added_aggregate_names):
350
385
relabels [None ] = 'subquery'
351
386
# Remove any aggregates marked for reduction from the subquery
352
387
# and move them to the outer AggregateQuery.
353
- for alias , annotation in inner_query . annotation_select . items ():
354
- if annotation . is_summary :
355
- # The annotation is already referring the subquery alias, so we
356
- # just need to move the annotation to the outer query.
357
- outer_query .annotations [alias ] = annotation .relabeled_clone (relabels )
388
+ col_cnt = 0
389
+ for alias , expression in inner_query . annotation_select . items () :
390
+ if expression . is_summary :
391
+ expression , col_cnt = inner_query . rewrite_cols ( expression , col_cnt )
392
+ outer_query .annotations [alias ] = expression .relabeled_clone (relabels )
358
393
del inner_query .annotation_select [alias ]
359
394
try :
360
395
outer_query .add_subquery (inner_query , using )
@@ -1495,6 +1530,10 @@ def resolve_ref(self, name, allow_joins, reuse, summarize):
1495
1530
raise FieldError ("Joined field references are not permitted in this query" )
1496
1531
if name in self .annotations :
1497
1532
if summarize :
1533
+ # Summarize currently means we are doing an aggregate() query
1534
+ # which is executed as a wrapped subquery if any of the
1535
+ # aggregate() elements reference an existing annotation. In
1536
+ # that case we need to return a Ref to the subquery's annotation.
1498
1537
return Ref (name , self .annotation_select [name ])
1499
1538
else :
1500
1539
return self .annotation_select [name ]
0 commit comments