【问题标题】:SQLAlchemy relationships across multiple tables跨多个表的 SQLAlchemy 关系
【发布时间】:2018-11-04 07:46:08
【问题描述】:

我正在尝试创建一个跨越 3 个表的关系,但我不太清楚语法。

我有 3 个表 TableATableBTableC,我要建模的关系是:

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

这样在TableA 的实例上,我可以使用instance_of_a.my_relationship 来获取与instance_of_a 间接关联的TableC 记录

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:
    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)
    

    【讨论】:

    • 出于某种原因,一种关系定义方法是否优于另一种?方法二中对secondary 的显式引用直观地似乎比方法一中复杂的primaryjoin str 更清晰......但方法一似乎更强大。
    • 这有点奇怪,就次要而言,这不是最初打算使用“次要”的方式,例如它预计 FK 都在中间。我不确定这种“次要”关系是否真的能够正确地持续下去,例如它也可能需要 viewonly=True 。在构建像急切加载这样的东西时,ORM 也会做出不同的选择,在这方面,“辅助”版本可能会做出更好的决定,因为它知道 FROM 子句中有一个额外的表。
    • 已确认,加入的急切加载仅适用于第二个,第一个无法正确构建 FROM
    • 如果'b'表没有映射到类并且只是一个辅助表怎么办? “辅助”方法连接将如何改变?
    猜你喜欢
    • 2018-02-24
    • 2010-12-25
    • 1970-01-01
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-26
    相关资源
    最近更新 更多