Mixing Q objects and lookups with When conditionals

779 views
Skip to first unread message

Jason

unread,
Sep 21, 2017, 10:13:11 AM9/21/17
to Django users
I have a model in which I was trying to get a count for records that matched three variable values and found an interesting edge case not in the documents for conditional expressions.  Specifically, if you have to use a Q expression for a not query, you can't mix them with lookups.

This is all on Django 1.8 with python 2.7.12

for example, 

SomeModel.objects.aggregate(total = Count(Case(When(~Q(some_field__icontains = 'value'), field_two = 2, field_three = True, then = 1), output_field = IntegerField()))

will result in a TypeError

/home/jason/.virtualenvs/work/lib/python2.7/site-packages/django/db/models/expressions.pyc in __init__(self, condition, then, **lookups)
    687             condition, lookups = Q(**lookups), None
    688         if condition is None or not isinstance(condition, Q) or lookups:
--> 689             raise TypeError("__init__() takes either a Q object or lookups as keyword arguments")
    690         super(When, self).__init__(output_field=None)
    691         self.condition = condition

TypeError: __init__() takes either a Q object or lookups as keyword arguments

However, moving the original to a full ANDed Q expression like 

query  = ~Q(some_field__icontains = 'value') & Q(field_two = 2) & Q(field_three = True)
SomeModel.objects.aggregate(total = Count(Case(When(query, then = 1), output_field = IntegerField()))

works fine.  I couldn't find anything specifically about this in the docs, so is this explicit behavior or an undocumented issue?

Simon Charette

unread,
Sep 21, 2017, 10:34:08 AM9/21/17
to Django users
Hello Jason,

I don't think this is explicitly documented but I feel like the TypeError raised explicitly states that
this isn't supported:

__init__() takes either a Q object or lookups as keyword arguments

Did you end up in this situation by following along the documentation or trial and error?

Best,
Simon 

Jason

unread,
Sep 21, 2017, 11:01:48 AM9/21/17
to Django users
Hi Simon,

That's how I feel about it too, but couldn't find anything specifically related to it, thus prompting my question.

I basically fell into this because django lookups can't handle exclusion (NOT).  I suspect there's an alternative to use exclude before the aggregation, like 

SomeModel.objects.exclude(some_field__icontains = 'value').aggregate(total = Count(Case(When(field_two = 2, field_three = True, then = 1), output_field = IntegerField()))

but that's not exactly intuitive, and I ended up chaining Q expressions for the same result.


Reply all
Reply to author
Forward
0 new messages