[Django]-Django – Optimal way to sort models by boolean operation

4👍

Conditional expressions

One option for it is to use conditional expressions. They provide simple way of checking conditions and providing one of values depending on them. In your case it will look like:

sorted_things = things.annotate(diff=Case(When(n1=F('n2'), then=True), default=False, output_field=BooleanField())).order_by('diff')

Q and ExpressionWrapper

There is another, a bit hacky way, to achieve that by combining usage of Q and ExpressionWrapper.

In django, Q is intended to be used inside filter(), exclude(), Case etc. but it simply creates condition that apparently can be used anywhere. It has only one drawback: it doesn’t define what type is outputting (it’s always boolean and django can assume that in every case when Q is intended to be used.

But there comes ExpressionWrapper that allows you to wrap any expression and define it’s final output type. That way we can simply wrap Q expression (or more than one Q expresisons glued together using &, | and brackets) and define by hand what type it outputs.

Be aware that this is undocumented, so this behavior may change in future, but I’ve checked it using django versions 1.8, 1.11 and 2.0 and it works fine

Example:

sorted_things = things.annotate(diff=ExpressionWrapper(Q(n1=F('n2')), output_field=BooleanField())).order_by('diff')

2👍

You can work around it using Func() expressions.

from django.db.models import Func, F

class NotEqual(Func):
    arg_joiner = '<>'
    arity = 2
    function = ''

things = Thing.objects.annotate(diff=NotEqual(F('n1'), F('n2'))).order_by('diff')
👤Martin

Leave a comment