【问题标题】:SqlAlchemy Relationship from one "join" table to another "join" table从一个“连接”表到另一个“连接”表的 SqlAlchemy 关系
【发布时间】:2012-08-12 04:25:14
【问题描述】:

我正在测试设置 SQLAlchemy 以映射现有数据库。该数据库是很久以前由我们不再使用的以前的第 3 方应用程序自动建立的,因此未定义一些预期的东西,例如外键约束。该软件将管理所有这些关系。

它是一个节点式的生产跟踪数据库结构,具有父子关系。特定于节点类型的数据存在于它们自己的表中,并且有一个主关系表用于类型、uid、parentUid 等常见列...

结构是这样的……

  1. 基本上包含每个节点条目的层次结构表,具有主键“uid”、“type”枚举和用于引用父节点的“parentUid”。
  2. NodeA / NodeB / ... 表有一个“uid”,与层次结构表一一匹配

我从 SQLAlchemy 文档中收集到的是我应该尝试“加入”表。这是我目前所拥有的:

# has a column called 'parentUid'
hierarchy = Table('hierarchy', METADATA, autoload=True)

hier_fk = lambda: Column('uid', 
                          Integer, 
                          ForeignKey('hierarchy.uid'), 
                          primary_key=True)

nodeTypeA = Table('nodetype_a', METADATA, nodehier_fk(), autoload=True)
nodeTypeB = Table('nodetype_b', METADATA, nodehier_fk(), autoload=True)

Base = declarative_base()

class NodeA(Base):
    __table__ = join(hierarchy, nodeTypeA)

    id = column_property(hierarchy.c.uid, nodeTypeA.c.uid)
    uid = nodeTypeA.c.uid


class NodeB(Base):
    __table__ = join(hierarchy, nodeTypeB)

    id = column_property(hierarchy.c.uid, nodeTypeB.c.uid)
    uid = nodeTypeB.c.uid

    # cannot figure this one out
    parent = relationship("NodeA", primaryjoin="NodeB.parentUid==NodeA.id")

relationship 显然是错误的并且崩溃了。我尝试了一系列定义foreign_keys 属性的组合,并混合使用hierarchy.c.uid 样式方法。但我就是不明白如何与另一个表建立关系。

没有relationship 行,查询效果很好。我得到了加入层次表的每个节点的完整表示。我什至可以通过以下方式手动获取 NodeB 的 NodeA 父级:

node_a = session.query(NodeA).filter_by(uid=node_b.parentUid).first()

“加入”方法是否适合我的目标?我怎样才能让这种关系发挥作用?

更新

到目前为止,我已经设法通过以下方式建立了一种单向关系:

children = relationship("NodeB", 
                        primaryjoin="NodeB.parentUid==NodeA.id", 
                        foreign_keys=[hierarchy.c.parentUid],
                        # backref="parent"
            )

但是,如果我取消注释 backref 让它在 NodeB 上反转,我会得到:

ArgumentError: NodeA.children 和反向引用 NodeB.parent 都是 同一个方向。你的意思是设置 remote_side 在多对一方面?

【问题讨论】:

    标签: join sqlalchemy


    【解决方案1】:

    remote_side 用于自引用关系以区分哪一侧是“远程”。该标志在http://docs.sqlalchemy.org/en/latest/orm/relationships.html#adjacency-list-relationships 中描述。因为您将类直接映射到 join(),所以 SQLAlchemy 将每个 join() 视为映射表,并且检测到“自引用”条件,因为两个连接都依赖于同一个基表。如果您要使用连接表继承的常用模式(请参阅http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#joined-table-inheritance)构建此映射,则 relationship() 将有更多上下文来确定如何在没有显式 remote_side 参数的情况下进行连接。

    使用给定方法的完整示例:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    
    e = create_engine("sqlite://", echo=True)
    
    e.execute("""
    create table hierarchy (uid int primary key, parentUid int)
    """)
    
    e.execute("""
    create table nodetype_a (uid int primary key)
    """)
    
    e.execute("""
    create table nodetype_b (uid int primary key)
    """)
    
    Base = declarative_base()
    
    
    # has a column called 'parentUid'
    hierarchy = Table('hierarchy', Base.metadata, autoload=True, autoload_with=e)
    
    nodehier_fk = lambda: Column('uid',
                              Integer,
                              ForeignKey('hierarchy.uid'),
                              primary_key=True)
    
    nodeTypeA = Table('nodetype_a', Base.metadata, nodehier_fk(), autoload=True, autoload_with=e)
    nodeTypeB = Table('nodetype_b', Base.metadata, nodehier_fk(), autoload=True, autoload_with=e)
    
    Base = declarative_base()
    
    class NodeA(Base):
        __table__ = join(hierarchy, nodeTypeA)
    
        id = column_property(hierarchy.c.uid, nodeTypeA.c.uid)
        uid = nodeTypeA.c.uid
    
    
    class NodeB(Base):
        __table__ = join(hierarchy, nodeTypeB)
    
        id = column_property(hierarchy.c.uid, nodeTypeB.c.uid)
        uid = nodeTypeB.c.uid
    
        # cannot figure this one out
        parent = relationship("NodeA",
                        primaryjoin="NodeB.parentUid==NodeA.id",
                        foreign_keys=hierarchy.c.parentUid,
                        remote_side=hierarchy.c.uid,
                        backref="children")
    
    s = Session(execute)
    s.add_all([
            NodeA(children=[NodeB(), NodeB()])
    ])
    s.commit()
    

    【讨论】:

    • 完美。那行得通。这只是属性的正确组合 :-) 谢谢!
    猜你喜欢
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 2021-06-22
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多