I would like to perform a logical exclusive OR ( XOR ) on django.db.models.Q objects, using operator to restrict the selection of the model field to a subset of the foreign key. I do this in Django 1.4.3 along with Python 2.7.2. I had something like this:
import operator from django.conf import settings from django.db import models from django.db.models import Q from django.contrib.auth.models import User, Group def query_group_lkup(group_name): return Q(user__user__groups__name__exact=group_name) class Book(models.Model): author = models.ForeignKey( User, verbose_name=_("Author"), null=False, default='', related_name="%(app_label)s_%(class)s_author",
AUTHORIZED_AUTHORS - a list of existing group names.
But this did not work, because the Q objects do not support the ^ operator (only | and the operators from docs ). The message from stacktrace was (partially) as follows:
File "/home/moi/.virtualenvs/venv/lib/python2.7/site-packages/django/db/models/loading.py", line 64, in _populate self.load_app(app_name, True) File "/home/moi/.virtualenvs/venv/lib/python2.7/site-packages/django/db/models/loading.py", line 88, in load_app models = import_module('.models', app_name) File "/home/moi/.virtualenvs/venv/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module __import__(name) File "/opt/dvpt/toto/apps/book/models.py", line 42, in <module> class Book(models.Model): File "/opt/dvpt/toto/apps/book/models.py", line 100, in Book map(query_group_lkup, getattr(settings, 'AUTHORIZED_AUTHORS', '')) TypeError: unsupported operand type(s) for ^: 'Q' and 'Q'
Therefore, inspired by this answer, I tried to implement XOR for my specific search. This is not very flexible since the search is hard-coded (I will need to use kwargs in the query_xor arguments, for example ...). I ended up doing something like this:
from django.conf import settings from django.db import models from django.db.models import Q from django.db.models.query import EmptyQuerySet from django.contrib.auth.models import User, Group def query_xor_group(names_group): """Get a XOR of the queries that match the group names in names_group.""" if not len(names_group): return EmptyQuerySet() elif len(names_group) == 1: return Q(user__user__groups__name__exact=names_group[0]) q_chain_or = Q(user__user__groups__name__exact=names_group[0]) q_chain_and = Q(user__user__groups__name__exact=names_group[0]) for name in names_group[1:]: query = Q(user__user__groups__name__exact=name) q_chain_or |= query q_chain_and &= query return q_chain_or & ~q_chain_and class Book(models.Model): author = models.ForeignKey( User, verbose_name=_("author"), null=False, default='', related_name="%(app_label)s_%(class)s_author",
It works the way I want, but it seems to me rather not pythonic (especially the query_xor_group method). Would it be better (a more direct way) to do this?
Basically, my question may be deprived of the limit_choices_to element and summarized as:
How can I do a bitwise exclusive OR on a lot of django.db.models.Q objects in Djangonic?