我想对
django.db.models.Q对象执行逻辑异或(
XOR),使用
operator模块将模型字段的选择限制为外键的子集.我在Django 1.4.3和Python 2.7.2中这样做.我有这样的事情:
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是现有组名的列表.
但这不起作用,因为Q对象不支持^运算符(仅来自docs的|和&运算符).来自stacktrace的消息(部分)如下:
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,'')) TypeError: unsupported operand type(s) for ^: 'Q' and 'Q'
因此,受到answer的启发,我试图为我的特定查找实现XOR.它不是很灵活,因为查找是硬编码的(我需要在query_xor的参数中使用kwargs,例如……).我最终做了这样的事情:
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"),# This provides an exclusive OR on the SELECT group name for User limit_choices_to=query_xor_group(getattr(settings,'')) )
它按我的意思工作,但我觉得我不是pythonic(尤其是query_xor_group方法).
这样做会有更好的(更直接的方式)吗?
基本上,我的问题可以被剥夺limit_choices_to部分,并总结为:
如何以Djangonic方式对一组django.db.models.Q对象进行按位异或?
解决方法
您可以向使用和/或/不执行XOR逻辑的Q添加__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,)
在这之后我能够在filter()调用中进行Q(…)^ Q(…).
Foobar.objects.filter(Q(blah=1) ^ Q(bar=2))
这意味着原始尝试不再抛出不受支持的操作数异常.
limit_choices_to=reduce( operator.xor,'')) )
在Python 2.7.5上测试Django 1.6.1