SqlAlchemy从一个“join”表到另一个“join”表的关系

时间:2012-08-10 21:40:31

标签: join sqlalchemy

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

它是一个node-ish类型的生产跟踪数据库结构,具有父子关系。特定于节点类型的数据存在于它们自己的表中,并且存在公共列的主关系表,如type,uid,parentUid,...

结构是这样的......

  1. 一个层次结构表,基本上包含每个节点条目,主键为“uid”,“type”枚举,以及引用父节点的“parentUid”。
  2. NodeA / NodeB / ...表有一个“一对一”匹配层次表的“uid”
  3. 我从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和back-reference NodeB.parent都是   相同的方向。你的意思是设置   remote_side在多对一方面?

1 个答案:

答案 0 :(得分:1)

remote_side用于自引用关系,以区分哪一方是“远程”。该标志在http://docs.sqlalchemy.org/en/latest/orm/relationships.html#adjacency-list-relationships中描述。因为您将类直接映射到join(),所以SQLAlchemy将每个join()视为映射表,并且检测到“is self referential”条件,因为两个连接都依赖于相同的基表。如果您使用通常的连接表继承模式构建此映射(请参阅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()