2012-06-23 4 views
0

내 모델에는 특성 집합을 보유하는 기본 개체 클래스 A이 있습니다. A의 각 객체는 many-to-many 연관 객체 Context을 통해 A의 다른 객체에 연결할 수 있습니다. 이 Context 클래스에는 모든 연결에 대한 키가 있습니다.자기 참조 다 대다 SQLAlchemy 연결에 대한 dict-set 프록시

class A(Base): 
    __tablename__ = "a" 
    id = Column(Integer, primary_key=True) 

    # other attributes 
    value = Column(Integer) 

    def __init__(self, value): 
     self.value = value 

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) 

Context 클래스 따라서 저장 형태 3- 튜플 '홀더 객체 a1k1 함께 첨부 a2 보유'.

지금 다음과 같이 Context.key 등의 관계 내가 그것을 사용할 수있는 그룹 A에 스마트 프록시 컬렉션이 할

:

a1 = A(1) 
a1.context["key_1"] = set([A(2)]) 
a1.context["key_2"] = set([A(3), A(4), A(5)]) 

a1.context["key_1"].add(A(10)) 

a100 = A(100) 
a100.context = { 
    "key_1": set([A(101)]) 
} 

을 내가 context을 정의해야합니까?

SQLAlchemy 설명서에서 dict-set 프록시를 모델링하는 예제가 있지만 어쨌든 자체 참조 환경에서 작동하도록 할 수는 없습니다.

답변

1

내가 블랭킹하지 않는 한 (가능한 ... 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]) 
+0

워를 : 너무 큰 거래, 그냥 당신이 여기에 모든 조작에 대한 테스트를 추가 할 수 있도록, 크랭크 .... 꽤 설정해야합니다. 많은 감사합니다. Holder 클래스를'collections.MutableMapping' /'collections.MutableSet' 클래스로 만들지 않는 이유가 있습니까? – Debilski

+0

이유가 없기 때문에, 나는 당신이 구현 한 방법에 대해 많은 것을 생각하는 것처럼 과거에 이러한 클래스가 까다로웠다는 것을 알았으므로 여기서 신경 쓰지 않았습니다. – zzzeek

+0

나는이 답변에 대해 당신에게 현상금을 수여하고있다. (23 시간 후, 나는 짐작한다.) 한가지 더 질문한다. 당신은'HolderBySet'에서'items' 속성을 만들지 않습니다. 이것은 감독이며, 요청할 때'items'는 항상 다시 계산되어야합니다 (그렇지 않으면'HolderBySet'에서 몇몇 methos를 최적화 할 수 있습니다). – Debilski

관련 문제