Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - an equality check in annotate-clause

Tags:

django

In my Django 1.9 project I am trying to output something like this:

return MyModel.objects.values(...).\
               annotate(flg = ExpressionWrapper(F('rgt') - F('lft') > 0, 
                        output_field = BooleanField()))

This yields AttributeError: 'bool' object has no attribute 'resolve_expression' error.

I try to rewrite it using Case-when syntax:

return MyModel.objects.values(...)\
              .annotate(flg = Case(When(F('rgt') - F('lft') > 0, then = True, 
               output_field = BooleanField())))

This time, I end up with TypeError: __init__() takes either a Q object or lookups as keyword arguments error. I am lost. Any ideas ?

like image 725
Crazy Frog Avatar asked Feb 19 '17 20:02

Crazy Frog


2 Answers

Try:

from django.db.models import Q, F

...

return MyModel.objects.values(...).\
               annotate(flg=ExpressionWrapper(Q(rgt=F('lft')), 
                        output_field=BooleanField()))

Feature request to make it easier: https://code.djangoproject.com/ticket/27021

like image 200
Dmitry Mugtasimov Avatar answered Oct 18 '22 22:10

Dmitry Mugtasimov


Django expressions don't support the comparison operators using standard syntax.

Check https://docs.djangoproject.com/en/stable/ref/models/expressions/#supported-arithmetic

However, you can use Func() expressions: https://docs.djangoproject.com/en/stable/ref/models/expressions/#func-expressions

.annotate(flg=Func(F('rgt') + F('lft'), template='%(expressions)s > 0'))

Or even better, define a Function / Transform class:

class GreaterThanZero(Transform):
    template = '%(expressions)s > 0'

.annotate(flg=GreaterThanZero(F('rgt') + F('lft')))

Transforms are unary operators, extending Transform you can ensure that only one expression is passed as an argument. So that you couldn't do GreaterThanZero(F('rgt') + F('lft'), 1, 2, 3, 14).

Obviously, a more flexible solution should allow to pass both sides of the comparison. Somethin like this:

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

.annotate(flg=GreaterThan(F('rgt') + F('lft'), 0))
like image 27
JoseKilo Avatar answered Oct 18 '22 23:10

JoseKilo