I need to order Django queryset by annotated field and then annotate result with row number.
Steps:
- Add
complex_orderfield to QuerySet that defines extra logic for future ordering. - Order QuerySet by new field and old fields (
complex_order,orderandid) - Annotate sorted QuerySet with
numberusingRowNumber()function.
Sample code that almost works
queryset.annotate(
complex_order=Case(
When(
Q(foo__type=FooType.ONE.value) | Q(foo__isnull=True)
then=Value(1),
),
default=Value(0),
),
).order_by(
'-complex_order', 'order', 'id'
).annotate(
number=Window(expression=RowNumber())
)
But I have a problem with wrong number annotation. After inspecting the SQL query I noticed that annotation happens at the same time with ordering.
SELECT "table.id", ...
CASE WHEN ("foo"."type" = ONE OR ("foo" IS NULL))
THEN 1 ELSE 0 END AS "complex_order", ROW_NUMBER() OVER () AS "number"
FROM ...
ORDER BY 32 DESC, "table"."order" ASC, "table"."id" ASC
I need to annotate queryset second time, right after sorting it. Or maybe there is a better way to add iterable number for each row in queryset?
The solution was a bit different than I thought.
Firstly we annotate QuerySet with
complex_order, then annotate it withnumberusingorder_byinside window function. And finally we order QuerySet bynumberfield.Result examples:
Wrong number annotation (result of code from question)
Correct number annotation (result of code from answer)