Realizar un OR exclusivo lógico en un objeto Django Q

Me gustaría realizar un OR exclusivo lógico ( XOR ) en objetos django.db.models.Q, utilizando el módulo operador para limitar las opciones de un campo de modelo a un subconjunto de clave foránea. Estoy haciendo esto en Django 1.4.3 junto con Python 2.7.2. Tuve algo como esto:

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", # This would have provide an exclusive OR on the selected group name for User limit_choices_to=reduce( operator.xor, map(query_group_lkup, getattr(settings, 'AUTHORIZED_AUTHORS', '')) ) 

AUTHORIZED_AUTHORS es una lista de nombres de grupos existentes.

Pero esto no funcionó, porque los objetos Q no son compatibles con el operador ^ (solo | y operadores de los documentos ). El mensaje del stacktrace fue (en parte) lo siguiente:

 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  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' 

Por lo tanto, inspirado por esta respuesta , intenté implementar un XOR para mi búsqueda específica. No es realmente flexible ya que la búsqueda está codificada (necesitaría usar kwargs en los argumentos de query_xor por ejemplo …). Terminé haciendo algo como esto:

 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", # This provides an exclusive OR on the SELECT group name for User limit_choices_to=query_xor_group(getattr(settings, 'AUTHORIZED_AUTHORS', '')) ) 

Funciona como quiero, pero me parece que no es pythonic (especialmente el método query_xor_group). ¿Habría una forma mejor (más directa) de hacer esto?

Básicamente, mi pregunta puede ser eliminada de limit_choices_to parte y resumirse como:

¿Cómo puedo hacer un OR exclusivo a nivel de bit en un conjunto de objetos django.db.models.Q de forma Djangónica?

Podría agregar un __xor__() a Q que use y / o / no para hacer la lógica XOR.

 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, ) 

Después de hacer esto pude Q(...) ^ Q(...) en una llamada de filter() .

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

Lo que significa que el bash original ya no lanza una excepción de operando no compatible.

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

Probado en Django 1.6.1 en Python 2.7.5