2013-07-10 9 views
11

3つのテーブルにわたる関係を作成しようとしていますが、構文を理解できません。複数のテーブルにわたるSQLAlchemy関係

私は3つのテーブルTableATableBTableCを持っていると私はモデルにしようとしている関係は次のとおりです。TableAのインスタンス上で、私は間接的に関連するのですTableCレコードを取得するためにinstance_of_a.my_relationshipを行うことができるように

TableA.my_relationship = relationship(
    'TableC', 
    primaryjoin='and_(TableA.fk == TableB.pk, TableB.fk == TableC.pk)', 
    viewonly=True 
) 

with instance_of_a

答えて

15
from sqlalchemy import * 
from sqlalchemy.orm import * 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class A(Base): 
    __tablename__ = 'a' 

    id = Column(Integer, primary_key=True) 
    b_id = Column(Integer, ForeignKey('b.id')) 

    # method one - put everything into primaryjoin. 
    # will work for simple lazy loads but for eager loads the ORM 
    # will fail to build up the FROM to correctly include B 
    cs = relationship("C", 
       # C.id is "foreign" because there can be many C.ids for one A.id 
       # B.id is "remote", it sort of means "this is where the stuff 
       # starts that's not directly part of the A side" 
       primaryjoin="and_(A.b_id == remote(B.id), foreign(C.id) == B.c_id)", 
       viewonly=True) 

    # method two - split out the middle table into "secondary". 
    # note 'b' is the table name in metadata. 
    # this method will work better, as the ORM can also handle 
    # eager loading with this one. 
    c_via_secondary = relationship("C", secondary="b", 
         primaryjoin="A.b_id == B.id", secondaryjoin="C.id == B.c_id", 
         viewonly=True) 
class B(Base): 
    __tablename__ = 'b' 

    id = Column(Integer, primary_key=True) 
    c_id = Column(Integer, ForeignKey('c.id')) 

class C(Base): 
    __tablename__ = 'c' 
    id = Column(Integer, primary_key=True) 

e = create_engine("sqlite://", echo=True) 
Base.metadata.create_all(e) 

sess = Session(e) 

sess.add(C(id=1)) 
sess.flush() 
sess.add(B(id=1, c_id=1)) 
sess.flush() 
sess.add(A(b_id=1)) 
sess.flush() 

a1 = sess.query(A).first() 
print(a1.cs) 

print(a1.c_via_secondary) 
+0

何らかの理由で1つの関係定義方法が優先されますか?方法2の「二次」の明示的な参照は、方法1の複雑な「一次結合」の文字列よりも直観的にはっきりしているように思われますが、方法1はもっと強力であるようです。 – Russ

+0

これは一種の奇妙なもので、二次的なもので、二次的なものがもともと使われることを意図したものではありません。それはFKsが真ん中にあることを期待する。その「セカンダリ」関係が実際に正しく維持されるかどうかはわかりません。 viewonly = Trueである必要があるかもしれません。 ORMはまた、熱心な負荷のようなものを構築する際にさまざまな選択を行い、FROM句に余分なテーブルがあることを知っているので、「セカンダリ」バージョンがより良い決定を下す可能性があります。 – zzzeek

+0

が確認され、熱心なローディングに参加したのは2番目のものだけで、最初のものは正しく – zzzeek

関連する問題