#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 django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
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