在sqlalchemy中,如何在子表有多个外键指向父表时使用多态连接表继承?

11 投票
1 回答
4381 浏览
提问于 2025-04-17 15:56

我有一个叫做 pbx_point 的父表,这个表里有一个叫 point_type 的列。还有一个叫做 pbx_route 的子表,里面有一个叫 point_id 的列,它指向 pbx_point 表。

我想用 sqlalchemy 的连接表继承功能,通过声明式基类把这两个表关联起来,并使用多态继承。

这本来是可以正常工作的,但有一个额外的限制:pbx_point 还有一个外键 initial_route_id,它指向 pbx_route 表。

我下面也在使用反射,但数据库结构如我所描述的那样。出现的错误是 sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'pbx_point' and 'pbx_route'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.

这很有道理,因为在后台,声明式基类正在为这两个映射类创建一个 relationship() 属性。我希望它选择 pbx_route.point_id 作为父 ID 的连接,但它也看到了 pbx_point.initial_route_id 这一列。如果我是在创建这个 relationship(),那就简单多了,但我并不是这样做的,而是声明式继承系统在处理这件事。

我能否向 __mapper_args__ 传递一个额外的参数,比如 polymorphic_parent_col,来指定我想要的外键?如果不能,我该如何解决这个问题呢?

谢谢。

class MyBase(DeferredReflection):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

Base = declarative_base(cls=MyBase)

class pbx_point(Base):
    __mapper_args__ = dict(
        polymorphic_on='point_type',
        with_polymorphic='*',
    )

class pbx_route(pbx_point):
    __mapper_args__ = dict(polymorphic_identity='pbx.route')

这是我得到的堆栈跟踪信息:

Traceback (most recent call last):
  File "db.py", line 50, in <module>
    Base.prepare(engine)
  File "env/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/api.py", line 431, in prepare
    thingy.map()
  File "env/local/lib/python2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 379, in map
    **mapper_args
  File "env/local/lib/python2.7/site-packages/sqlalchemy/orm/__init__.py", line 1147, in mapper
    return Mapper(class_, local_table, *args, **params)
  File "env/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 213, in __init__
    self._configure_inheritance()
  File "env/local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py", line 517, in _configure_inheritance
    self.local_table)
  File "env/local/lib/python2.7/site-packages/sqlalchemy/sql/util.py", line 397, in join_condition
    "join explicitly." % (a.description, b.description))
sqlalchemy.exc.AmbiguousForeignKeysError: Can't determine join between 'pbx_point' and 'pbx_route'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.

这表明它在 mapper.py 的第 1032 行 出现了问题。上面几行提到了 mapper 的关键字参数 inherit_condition,这似乎正是我需要的。

[*]: 源链接已调整为版本 1.3.11(之前的链接现在已失效)

1 个回答

13

关键在于 inherit_condition 这个参数,它是给映射器用的。多态的信息其实和这个步骤没有直接关系。

修正后的模型:

class MyBase(DeferredReflection):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

Base = declarative_base(cls=MyBase)

class pbx_point(Base):
    __mapper_args__ = dict(
        polymorphic_on='point_type',
        with_polymorphic='*',
    )
    id = Column(Integer, primary_key=True)

class pbx_route(pbx_point):
    point_id = Column(Integer, ForeignKey(pbx_point.id))
    __mapper_args__ = dict(
        polymorphic_identity='pbx.route',
        inherit_condition=(point_id == pbx_point.id)
    )

我需要添加 idpoint_id 这两列,以便在 inherit_condition 参数中使用它们。可能有办法只用反射来做到这一点,但这并不是一个大问题。

撰写回答