Python 对Django Q对象执行逻辑异或
我想对django.db.models.Q对象执行逻辑异或(),使用模块将模型字段的选择限制为foreignkey的子集。我在Django 1.4.3和Python2.7.2中实现了这一点。我有这样的想法:Python 对Django Q对象执行逻辑异或,python,django,django-queryset,django-q,Python,Django,Django Queryset,Django Q,我想对django.db.models.Q对象执行逻辑异或(),使用模块将模型字段的选择限制为foreignkey的子集。我在Django 1.4.3和Python2.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 impo
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', ''))
)
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', ''))
)
AUTHORIZED_AUTHORS
是现有组名的列表
但这不起作用,因为Q对象不支持^
运算符(仅支持来自的| and&运算符)。来自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, getattr(settings, 'AUTHORIZED_AUTHORS', ''))
TypeError: unsupported operand type(s) for ^: 'Q' and 'Q'
它可以按照我的意愿工作,但在我看来,它不是pythonic(尤其是query\uxor\ugroup方法)。
有没有更好(更直接)的方法
基本上,我的问题可以去掉部分的限制,总结如下:
如何以Djangonic方式在一组django.db.models.Q对象上生成按位异或?您可以向Q添加一个
\uuuuxor\uuxor()
方法,该方法使用和/或/不执行异或逻辑
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,
map(query_group_lkup, getattr(settings, 'AUTHORIZED_AUTHORS', ''))
)
在
Python2.7.5
上的Django 1.6.1
中测试,这真是太棒了!Thanks@DanR:您能用完整的示例详细说明您的解决方案吗?添加该类只会让askers第一次尝试成功,但我还介绍了如何在任何筛选器调用中使用它。虽然这确实有效,但我认为最好避免在python中使用像这样的monkeypatching,因为现在很难跟踪何时应用此修补程序(不过,我想你可以看看QQ
是否在Q.mro()
).IMO,最好使用QQ
子类Q
,并使用该子类;如果这样可以使事情更加一致,您可以全局使用该子类。此外,如果您还没有这样做,您肯定应该发起将该子类拉入django的请求。