내가 블랭킹하지 않는 한 (가능한 ... gin ...) a1.context는 각 요소가있는 컬렉션이어야하므로 관계에 직접 연관 프록시를 사용하여 수행 할 수 없습니다. 고유 한 키이면 콜렉션은 사전이 될 수 있지만 a1은 동일한 키를 가진 많은 Context 객체를 가질 수 있기 때문에 그러한 콜렉션은 없다. Assoc prox는 각 객체의 속성 집합에 객체 컬렉션을 줄이는 간단한 방법을 사용하지 않습니다.
당신이 정말로 이것을 원한다면 구조가 바뀌지 않을 것입니다. 연관 프록시가하는 일을 단지 하드 코딩 된 방식으로 수행하십시오. 즉, 프록시 콜렉션을 만드십시오! 사실 두 사람이라고 생각합니다.
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
import itertools
Base= declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
# other attributes
value = Column(Integer)
def __init__(self, value):
self.value = value
@property
def context(self):
return HolderBySetDict(self)
@context.setter
def context(self, dict_):
toremove = set([ctx for ctx in self.attached_by if ctx.key not in dict_])
toadd = set([Context(key=k, holder=item) for k, v in dict_.items()
for item in itertools.chain(v)])
self.attached_by.update(toadd)
self.attached_by.difference_update(toremove)
class HolderBySetDict(object):
def __init__(self, parent):
self.parent = parent
def __iter__(self):
return iter(self.keys())
def keys(self):
return list(set(ctx.key for ctx in self.parent.attached_by))
def __delitem__(self, key):
toremove = set([ctx for ctx in self.parent.attached_by if ctx.key == key])
self.parent.attached_by.difference_update(toremove)
def __getitem__(self, key):
return HolderBySet(self.parent, key)
def __setitem__(self, key, value):
current = set([ctx for ctx in self.parent.attached_by if ctx.key == key])
toremove = set([ctx for ctx in current if ctx.holder not in value])
toadd = set([Context(key=key,holder=v) for v in value if v not in current])
self.parent.attached_by.update(toadd)
self.parent.attached_by.difference_update(toremove)
# exercises ! for the reader !
#def __contains__(self, key):
#def values(self):
#def items(self):
# ....
class HolderBySet(object):
def __init__(self, parent, key):
self.key = key
self.parent = parent
def __iter__(self):
return iter([ctx.holder for ctx
in self.parent.attached_by if ctx.key == self.key])
def update(self, items):
curr = set([ctx.holder for ctx
in self.parent.attached_by if ctx.key==self.key])
toadd = set(items).difference(curr)
self.parent.attached_by.update(
[Context(key=self.key, holder=item) for item in toadd])
def remove(self, item):
for ctx in self.parent.attached_by:
if ctx.key == self.key and ctx.holder is item:
self.parent.attached_by.remove(ctx)
break
else:
raise ValueError("Value not present")
def add(self, item):
for ctx in self.parent.attached_by:
if ctx.key == self.key and ctx.holder is item:
break
else:
self.parent.attached_by.add(Context(key=self.key, holder=item))
# more exercises ! for the reader !
#def __contains__(self, key):
#def union(self):
#def intersection(self):
#def difference(self):
#def difference_update(self):
# ....
class Context(Base):
__tablename__ = "context"
holder_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
attachment_id = Column(Integer, ForeignKey("a.id"), nullable=False, primary_key=True)
key = Column(String, primary_key=True)
holder = relationship(A,
primaryjoin=lambda: Context.holder_id==A.id)
attachment = relationship(A,
primaryjoin=lambda: Context.attachment_id==A.id,
backref=backref("attached_by", collection_class=set))
a1 = A(1)
a2 = A(2)
a3, a4, a5 = A(3), A(4), A(5)
a1.context["key_1"] = set([a2])
a1.context["key_2"] = set([a3, a4, a5])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"]) == set([a2])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_2"]) == set([a3, a4, a5])
a10 = A(10)
a1.context["key_1"].add(a10)
print set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"])
assert set([ctx.holder for ctx in a1.attached_by if ctx.key == "key_1"]) == set([a2, a10])
a100 = A(100)
a101 = A(101)
a100.context = {
"key_1": set([a101])
}
assert set([ctx.holder for ctx in a100.attached_by]) == set([a101])
워를 : 너무 큰 거래, 그냥 당신이 여기에 모든 조작에 대한 테스트를 추가 할 수 있도록, 크랭크 .... 꽤 설정해야합니다. 많은 감사합니다. Holder 클래스를'collections.MutableMapping' /'collections.MutableSet' 클래스로 만들지 않는 이유가 있습니까? – Debilski
이유가 없기 때문에, 나는 당신이 구현 한 방법에 대해 많은 것을 생각하는 것처럼 과거에 이러한 클래스가 까다로웠다는 것을 알았으므로 여기서 신경 쓰지 않았습니다. – zzzeek
나는이 답변에 대해 당신에게 현상금을 수여하고있다. (23 시간 후, 나는 짐작한다.) 한가지 더 질문한다. 당신은'HolderBySet'에서'items' 속성을 만들지 않습니다. 이것은 감독이며, 요청할 때'items'는 항상 다시 계산되어야합니다 (그렇지 않으면'HolderBySet'에서 몇몇 methos를 최적화 할 수 있습니다). – Debilski