Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django annotate - conditional count

I have a model called 'StoreItem' and a model named 'QuoteItem'. A QuoteItem points on a StoreItem.

I'm trying to annotate a counter of how many quote items point on store items, but with conditions to apply on the quote items.

I tried something like this:

items = items.annotate(
            quote_count=Count(
                Case(
                    When(quoteitem__lookup_date__in=this_week, then=1), 
                    output_field=IntegerField()
                )
            )
        )

'items' are a queryset of StoreItems. 'this_week' is a list of dates representing this week (that's the filter I try to apply). After I make the dates thing work I want to add more filters to this conditional count but lets start with that.

Anyway what I'm getting is more like a boolean - if Quote Items that match the condition exists, no matter how many I have, the counter will be 1. else, will be 0.

It looks like the Count(Case()) only check if any item exist and if so return 1, while I want it to iterate over all quote items that point on the store item and count them, if they match the condition (individually).

How do I make it happen?

like image 690
Ronen Ness Avatar asked Jun 28 '16 17:06

Ronen Ness


People also ask

What is annotate() in Django?

In the Django framework, both annotate and aggregate are responsible for identifying a given value set summary. Among these, annotate identifies the summary from each of the items in the queryset. Whereas in the case of aggregate, the summary is calculated for the entire queryset.

What is difference between annotate and aggregate in Django?

Unlike aggregate() , annotate() is not a terminal clause. The output of the annotate() clause is a QuerySet ; this QuerySet can be modified using any other QuerySet operation, including filter() , order_by() , or even additional calls to annotate() .

What is queryset annotate?

What is an annotation? Annotate generate an independent summary for each object in a queryset. In simpler terms, annotate allows us to add a pseudo field to our queryset. This field can then further be used for filter lookups or ordering.\

What is OuterRef Django?

to Django users. According to the documentation on models. OuterRef: It acts like an F expression except that the check to see if it refers to a valid field isn't made until the outer queryset is resolved.


1 Answers

You need to wrap everything in a Sum statement instead of Count (I find it a bit odd that Count works at all):

from django.db.models import Case, IntegerField, Sum, When

items = items.annotate(
        quote_count=Sum(
            Case(
                When(quoteitem__lookup_date__in=this_week, then=1), 
                output_field=IntegerField()
            )
        )
    )

This basically adds up all the 0s and 1s for the inner Case statement, resulting in a count of the number of matches.

like image 197
solarissmoke Avatar answered Sep 29 '22 11:09

solarissmoke