Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: delete auxiliary annotation from queryset

I am building a complicated queryset over a complicated datamodel. The queryset must return a selection of model objects A with an annotation ann.

For computing ann, I need an auxiliary annotation aux, but no aux is allowed to exist in the final results of the queryset.

qs = A.objects.filter(...)  # complicated
qs = qs.annotate(aux=...)  # complicated
qs = qs.annotate(ann=Case(
    When(condition=Q(aux=0), 
         then Value('some')),
    When(condition=Q(aux_gt=0),
         then Value('other'))))

How to return qs with ann but without aux?

(Alternatively: If aux is a count and ann discriminates zero and non-zero aux, is there a better way to approach the whole problem?)

like image 539
Lutz Prechelt Avatar asked Oct 11 '25 20:10

Lutz Prechelt


2 Answers

Django 3.2 introduced a feature to solve specifically this:

https://docs.djangoproject.com/en/3.2/ref/models/querysets/#alias

alias() works like annotate() except only for other queryset methods such as annotate(): it doesn't stay in the final output.

qs = A.objects.filter(...)
qs = qs.alias(aux=...)  # using alias here instead of annotate
qs = qs.annotate(ann=Case(
When(condition=Q(aux=0), 
     then Value('some')),
When(condition=Q(aux_gt=0),
     then Value('other'))))
like image 153
Cameron Lee Avatar answered Oct 14 '25 16:10

Cameron Lee


Ugly hack:

A simple, if highly unelegant method is to rename aux to be called ann as well. That way, the second annotation overwrites the first and the first will no longer exist in the output.

qs = A.objects.filter(...)  # complicated
qs = qs.annotate(ann=...)  # complicated
qs = qs.annotate(ann=Case(
    When(condition=Q(ann=0), 
         then Value('some')),
    When(condition=Q(ann_gt=0),
         then Value('other'))))

This appears to work. Tested with Django 1.11 on SQlite and PostgreSQL.

But there must be a better way, pleeeease.

like image 24
Lutz Prechelt Avatar answered Oct 14 '25 17:10

Lutz Prechelt