3

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

它是一种节点式的生产跟踪数据库结构,具有父子关系。特定于节点类型的数据存在于它们自己的表中,并且有一个用于常见列(如 type、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 吗?

4

1 回答 1

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()
于 2012-08-11T20:25:14.390 回答