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)
何らかの理由で1つの関係定義方法が優先されますか?方法2の「二次」の明示的な参照は、方法1の複雑な「一次結合」の文字列よりも直観的にはっきりしているように思われますが、方法1はもっと強力であるようです。 – Russ
これは一種の奇妙なもので、二次的なもので、二次的なものがもともと使われることを意図したものではありません。それはFKsが真ん中にあることを期待する。その「セカンダリ」関係が実際に正しく維持されるかどうかはわかりません。 viewonly = Trueである必要があるかもしれません。 ORMはまた、熱心な負荷のようなものを構築する際にさまざまな選択を行い、FROM句に余分なテーブルがあることを知っているので、「セカンダリ」バージョンがより良い決定を下す可能性があります。 – zzzeek
が確認され、熱心なローディングに参加したのは2番目のものだけで、最初のものは正しく – zzzeek