[Solved]-Perform a logical exclusive OR on a Django Q object

14👍

You could add an __xor__() method to Q that uses and/or/not to do the XOR logic.

from django.db.models import Q

class QQ:
    def __xor__(self, other):    
        not_self = self.clone()
        not_other = other.clone()
        not_self.negate()
        not_other.negate()

        x = self & not_other
        y = not_self & other

        return x | y

Q.__bases__ += (QQ, )

After doing this I was able to Q(...) ^ Q(...) in a filter() call.

Foobar.objects.filter(Q(blah=1) ^ Q(bar=2)) 

Which means the original attempt no longer throws an unsupported operand exception.

limit_choices_to=reduce(
                     operator.xor,
                     map(query_group_lkup, getattr(settings, 'AUTHORIZED_AUTHORS', ''))
                 )

Tested in Django 1.6.1 on Python 2.7.5

11👍

Django 4.1 added support for XOR:

Q objects and querysets can now be combined using ^ as the exclusive or (XOR) operator. XOR is natively supported on MariaDB and MySQL. For databases that do not support XOR, the query will be converted to an equivalent using AND, OR, and NOT.

It means you can now write Foobar.objects.filter(Q(blah=1) ^ Q(bar=2)) without monkey patching.
It was worth waiting nine years, wasn’t it?

    Leave a comment