4

How to sum a field of a subquery ( with OuterRef ) and annotate it on outer model?

note that I have a common my_special_queryset_annotator that alter queryset by adding some annotations, ... so i dont want to use direct Sum('books__sections__page')

let assume following models

class Library(models.Model):
    votes=models.IntegerField()

class Book(models.Model):
    library=models.ForiegnKey(Library)

class Section(models.Model):
    book=models.ForiegnKey(Book)
    pages=models.IntegerField()

# this works, but when want to use `my_special_queryset_annotator` 
# we could not do this simple annotation
Library.annotate(
    all_pages=Sum('books__sections__pages'),
)

# when want to sum on a Subquery, its must constructed like below but it dont work
Library.objects.annotate(
    all_pages=SUM(  # <-- problem
        Subquery(
            my_special_queryset_annotator(
                Section.objects.filter(book__libraray_id=OuterRef('id'))
            ).values('altered_pages')
        )
    )
)
3
  • The Sum should be inside the subquery, as shown here Commented Sep 17, 2019 at 16:15
  • So: all_pages=Subquery(qs.annotate(total_pages=Sum('pages')).values('total_pages')[:1]) Commented Sep 17, 2019 at 16:16
  • i have tested this before and this is not working correct, specially with OuterRef, it return only first section pages as all_pages. Commented Sep 18, 2019 at 22:06

1 Answer 1

5

one try that solve the problem in ugly way is to create something like below, but i cant configure how not to pass sum_field parameter to it and just use .values(sum_field) on given queryset

class SumSubquery(Subquery):
    template = "(SELECT SUM(%(sum_field)s) FROM (%(subquery)s) _sum)"
    output_field = models.DecimalField()

    def __init__(self, queryset, output_field=None, *, sum_field, **extra):
        extra['sum_field'] = sum_field
        super(SumSubquery, self).__init__(queryset, output_field, **extra)
# and use like below

Library.objects.annotate(
    all_pages=SumSubquery(
        my_special_queryset_annotator(
            Section.objects.filter(book__libraray_id=OuterRef('id'))
        ),
        sum_field='altered_pages',
    )
)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.