10

モデルフィールドの選択を外部キーのサブセットに制限するために演算子モジュールを使用して、オブジェクトに対して論理排他的 OR ( XOR )を実行したいと考えています。私はPython 2.7.2とともにDjango 1.4.3でこれを行っています。私はこのようなものを持っていました:django.db.models.Q

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ただし、オブジェクトは演算子をサポートしていないため、これは機能しませんでした (ドキュメントのand演算子^のみ)。スタック トレースからのメッセージは (部分的に) 次のとおりです。|&

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'

したがって、この回答に触発されて、特定のルックアップに XOR を実装しようとしました。ルックアップがハードコードされているため、実際には柔軟ではありません (たとえばkwargs、引数で使用する必要があります...)。query_xor私は最終的に次のようなことをしました:

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

それは私が望むように機能しますが、私にはpythonicではないようです(特にquery_xor_groupメソッド)。これを行うためのより良い(より直接的な方法)はありますか?

基本的に、私の質問はその部分を取り除き、次のlimit_choices_toように要約できます。

django.db.models.QDjangonic の方法で一連のオブジェクトに対してビット単位の排他的 OR を作成するにはどうすればよいですか?

4

1 に答える 1

13

__xor__()and/or/not を使用して XOR ロジックを実行するメソッドを Q に追加できます。

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

これを行った後、私は電話をかけることができましQ(...) ^ Q(...)filter()

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

これは、元の試行でサポートされていないオペランド例外がスローされなくなったことを意味します。

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

Django 1.6.1でテスト済みPython 2.7.5

于 2014-01-19T18:19:45.700 に答える