Hi everyone, @Matthew and @Simon Thanks for your answers!
> I assume you are trying to work > around the cross join of multiple table annotations? This is correct. > There's tickets tracking adding subquery support to aggregate functions but > using subqueries doesn't seem to be necessary here? Does this mean I can’t use subqueries in aggregate right now? (Doesn’t seem to be the case because when I replace the filter in my code below with the commented filter, the query produces expected results so far.) I had a look at following ticket but it seems to be another case: Add support for aggregation through subqueries https://code.djangoproject.com/ticket/28296 <https://code.djangoproject.com/ticket/28296> > You may want to consider conditional expressions (Case … When) to achieve > your goal. > https://docs.djangoproject.com/en/2.1/ref/models/conditional-expressions/ > <https://docs.djangoproject.com/en/2.1/ref/models/conditional-expressions/> These functions attracted my attention when I was looking for a solution for the described exception. I want to try to adjust my query and use them. (But I thought that what I found is a bug. Therefore, I decided to write this e-mail.) I will see if conditional expressions help. I actually didn’t want to bother you with the current code of my complete query but I should probably send it to you to understand why I use subqueries and filters that way. However, I’m not experienced with votes and such complex queries and there might be a better solution. But I didn’t want to ask you to take this work. This subquery is also used in other queries. Therefore, I placed it in a function: def get_vote_subquery(role, user=None): filter_fields = {'role': role} if user: filter_fields.update({'user': user}) subquery = Subquery( models.Vote.objects .filter(segment=OuterRef('pk')) .values('segment_id') .annotate(sum=Sum('value', filter=Q(**filter_fields))) .values('sum')[:1] ) return subquery The following code is slightly simplified. I didn’t manage to get the necessary data with one query so far. (I’ll use Redis in the future to get the votes. Therefore, it doesn’t have to be the most effective query for now). @property def table_of_contents(self): if self._table_of_contents: return self._table_of_contents # Objects headings = list( models.Segment.objects .filter(tag__in=IMPORTANT_HEADINGS) .order_by('position') .values( 'pk', 'position', 'content', ) ) # Translation state (aggregation because annotation not possible) aggregates = {} required = REQUIRED_APPROVALS[self.language] for i, h in enumerate(headings, start=1): filters = {'position__gte': h['position']} try: filters['position__lt'] = headings[i]['position'] except IndexError: # The last item pass aggregates['translation_done_{}'.format(i)] = Count( 'progress', # this is a field of the model # filter=Q(**filters), filter=Q(**filters) & ( Q(progress__gte=TRANSLATION_DONE) | Q(reviewers_vote__gte=1) ), ) aggregates['review_done_{}'.format(i)] = Count( 'reviewers_vote', # filter=Q(**filters), filter=Q(reviewers_vote__gte=required['reviewer'], **filters), ) aggregates['trustee_done_{}'.format(i)] = Count( 'trustees_vote', # filter=Q(**filters), filter=Q(trustees_vote__gte=required['trustee'], **filters), ) aggregates = ( models.Segment.objects .annotate( reviewers_vote=get_vote_subquery('reviewer'), trustees_vote=get_vote_subquery('trustee'), ) .aggregate(**aggregates) ) # Assign some values for i, h in enumerate(headings, start=1): try: h['segments'] = headings[i]['position'] - h['position'] except IndexError: # The last item h['segments'] = self.segments_count - h['position'] + 1 h['translation_done'] = aggregates['translation_done_{}'.format(i)] h['review_done'] = aggregates['review_done_{}'.format(i)] h['trustee_done'] = aggregates['trustee_done_{}'.format(i)] self._table_of_contents = headings return self._table_of_contents Cheers, Daniel > Am 06.12.2018 um 06:37 schrieb Simon Charette <charett...@gmail.com>: > > Hello Daniel, > > It's hard to tell was causes the exception without digging a bit more but > based on your mention of multiple sums I assume you are trying to work > around the cross join of multiple table annotations? > > I understand you simplified your query but from what I can see right now > it can be expressed as > > commens_count = Comment.objects.annotate( > votes_count=Count('votes', filter=votes_filter), > ).filter( > votes_count__gte=4 > ).count() > > That should result in > > SELECT COUNT(*) FROM ( > SELECT 1 > FROM comment > LEFT OUTER JOIN vote ON (vote.comment_id = comment.id > <http://comment.id/>) > GROUP BY comment.id <http://comment.id/> > HAVING COUNT(*) FILTER (WHERE ...) > 4 > ) > > There's tickets tracking adding subquery support to aggregate functions but > using subqueries doesn't seem to be necessary here? > > Cheers, > Simon > > Le mercredi 5 décembre 2018 19:16:30 UTC-5, Daniel Gilge a écrit : > Hi, > > I think I've found a bug. But I'm not sure and I never opened a ticket for > Django before. So, I first wanted to ask here. > > I'm using Django 2.0 and this doesn't work: > > subquery = Subquery( > Vote.objects > .filter(comment=OuterRef('pk')) > .values('value')[:1] > ) > > Comment.objects.annotate(vote=subquery).aggregate( > count=Count('vote', filter=Q(vote__gte=4)), > ) > > It results in a quite useless AssertionError: > > django/db/models/expressions.py > > 168 > 169 def set_source_expressions(self, exprs): > --> 170 assert len(exprs) == 0 > 171 > 172 def _parse_expressions(self, *expressions): > > AssertionError: > > > Vars: > exprs > [Ref(__col8, Col(U0, myapp.Vote.comment))] > self > <django.db.models.expressions.Subquery object at 0x1080077b8> > > > It probably doesn't make sense because I simplified it. Why I'm using > subqueries is that I have several sums involved in the query: > > subquery = Subquery( > Vote.objects > .filter(comment=OuterRef('pk')) > .values('comment_id') > .annotate(sum=Sum('value', filter=Q(**filter_fields))) > .values('sum')[:1] > ) > > However, what I had to remove is a filter statement and then it works: > > Comment.objects.annotate(vote=subquery).aggregate( > count=Count('vote'), > ) > > Any advice appreciated! > > -- > You received this message because you are subscribed to a topic in the Google > Groups "Django users" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/django-users/JfZguPo-G_g/unsubscribe > <https://groups.google.com/d/topic/django-users/JfZguPo-G_g/unsubscribe>. > To unsubscribe from this group and all its topics, send an email to > django-users+unsubscr...@googlegroups.com > <mailto:django-users+unsubscr...@googlegroups.com>. > To post to this group, send email to django-users@googlegroups.com > <mailto:django-users@googlegroups.com>. > Visit this group at https://groups.google.com/group/django-users > <https://groups.google.com/group/django-users>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/django-users/44dcbf0e-e766-4ba9-a9e6-05ebf1a1709f%40googlegroups.com > > <https://groups.google.com/d/msgid/django-users/44dcbf0e-e766-4ba9-a9e6-05ebf1a1709f%40googlegroups.com?utm_medium=email&utm_source=footer>. > For more options, visit https://groups.google.com/d/optout > <https://groups.google.com/d/optout>. -- You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscr...@googlegroups.com. To post to this group, send email to django-users@googlegroups.com. Visit this group at https://groups.google.com/group/django-users. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/226ABB19-2300-450C-8FEC-DA844682493F%40gmail.com. For more options, visit https://groups.google.com/d/optout.