#30462: Aggregation with annotation(Subquery) throws AssertionError:
-------------------------------------+-------------------------------------
               Reporter:             |          Owner:  nobody
  rhals000327                        |
                   Type:             |         Status:  new
  Uncategorized                      |
              Component:  Database   |        Version:  2.2
  layer (models, ORM)                |       Keywords:  aggregation,
               Severity:  Normal     |  annotation, subquery
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 Aggregating+Count works fine when filtering argument exists in model.
 BUT if it doesn't exist in model it throws an AssertionError.

 I'll make an reproducible case below.
 models.py
 {{{
 class Highlight(models.Model):
    class Meta:
         managed = False
         db_table = 'test_highlight'
    content = models.CharField(max_length=100)
     lang = models.IntegerField(null=True, blank=True)

 class Page(models.Model):
    class Meta:
         managed = False
         db_table = 'test_Page'
    image = models.CharField(max_length=100)
    index = models.IntegerField()
    image_size = models.FloatField(default=1)
    text_id = models.PositiveIntegerField(blank=True, null=True,
 help_text='Includes Highlight ID')

 class Ebook(models.Model):
    class Meta:
         managed = False
         db_table = 'test_Book'
    title = models.CharField(max_length=100)
    page = models.ForeignKey(Page, related_name='book')

 class Company(models.Model):
    class Meta:
         managed = False
         db_table = 'test_company'
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(EBook, related_name='company')
    type = models.PositiveIntegerField(blank=True, null=True)
 }}}

 Our Mission is to get count of Specific Highlight Language Counts.
 And below is query for it
 {{{
 
Company.objects.annotate(lang=Subquery(Highlight.objects.filter(pk=OuterRef('books__page__text_id')).values('lang')).values('lang').filter(lang__isnull=False,
 lang__in=[1,2,3]).aggregate(highlight_count=Count('pk', distinct=True,
 filter=Q(lang=1))
 }}}

 :::Expected behaviour:::
 A dictionary is returned: {'hightlight_count': <value>}, containing value
 the number where Highlight_lang is 1.

 :::Actual behaviour:::
 Returns Assertion Error in query. No Exception Message is supplied for the
 error.

 Making a similiar query not using annotated field works like
 {{{
 
Company.objects.annotate(lang=Subquery(Highlight.objects.filter(pk=OuterRef('books__page__text_id')).values('lang')).values('lang').filter(lang__isnull=False,
 lang__in=[1,2,3]).aggregate(page_count=Count('pk', distinct=True,
 filter=Q(type__isnull=False, type=3))
 }}}

 ----
 Stacktrace
 {{{
 ~/{{ path }}lib/python3.7/site-packages/django/db/models/query.py in
 aggregate(self, *args, **kwargs)
     377             if not query.annotations[alias].contains_aggregate:
     378                 raise TypeError("%s is not an aggregate
 expression" % alias)
 --> 379         return query.get_aggregation(self.db, kwargs)
     380
     381     def count(self):

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/sql/query.py in
 get_aggregation(self, using, added_aggregate_names)
     459             for alias, expression in
 list(inner_query.annotation_select.items()):
     460                 if expression.is_summary:
 --> 461                     expression, col_cnt =
 inner_query.rewrite_cols(expression, col_cnt)
     462                     outer_query.annotations[alias] =
 expression.relabeled_clone(relabels)
     463                     del inner_query.annotations[alias]

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/sql/query.py in
 rewrite_cols(self, annotation, col_cnt)
     389                 # copied from the else clause, but this condition
 must appear
     390                 # before the contains_aggregate/is_summary
 condition below.
 --> 391                 new_expr, col_cnt = self.rewrite_cols(expr,
 col_cnt)
     392                 new_exprs.append(new_expr)
     393             elif isinstance(expr, Col) or (expr.contains_aggregate
 and not expr.is_summary):

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/sql/query.py in
 rewrite_cols(self, annotation, col_cnt)
     389                 # copied from the else clause, but this condition
 must appear
     390                 # before the contains_aggregate/is_summary
 condition below.
 --> 391                 new_expr, col_cnt = self.rewrite_cols(expr,
 col_cnt)
     392                 new_exprs.append(new_expr)
     393             elif isinstance(expr, Col) or (expr.contains_aggregate
 and not expr.is_summary):

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/sql/query.py in
 rewrite_cols(self, annotation, col_cnt)
     402                 # Some other expression not referencing database
 values
     403                 # directly. Its subexpression might contain Cols.
 --> 404                 new_expr, col_cnt = self.rewrite_cols(expr,
 col_cnt)
     405                 new_exprs.append(new_expr)
     406         annotation.set_source_expressions(new_exprs)

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/sql/query.py in
 rewrite_cols(self, annotation, col_cnt)
     404                 new_expr, col_cnt = self.rewrite_cols(expr,
 col_cnt)
     405                 new_exprs.append(new_expr)
 --> 406         annotation.set_source_expressions(new_exprs)
     407         return annotation, col_cnt
     408

 ~/{{ path }}lib/python3.7/site-packages/django/db/models/expressions.py in
 set_source_expressions(self, exprs)
     172
     173     def set_source_expressions(self, exprs):
 --> 174         assert not exprs
     175
     176     def _parse_expressions(self, *expressions):

 AssertionError:
 }}}

 Tested with three django versions. - 2.2.1 / 2.2 / 2.1
 None of those worked
 Python - 3.7.5

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30462>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/054.5c9fd6e60cf9d94bd42fdb590e71724e%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to