SQLAlchemy 单表继承中的一对多关系 - 声明式

17 投票
1 回答
10787 浏览
提问于 2025-04-16 22:01

基本上,我有一个模型,在一个表里映射了一个“BaseNode”类和两个子类。关键是我需要其中一个子类与另一个子类之间建立一种一对多的关系。简单来说,就是在同一个表里,不同类的行之间有关系。

你觉得我该如何用声明式语法来写这个呢?

注意:由于我模型中的其他关系,如果可以的话,我真的需要坚持使用单表继承。

class BaseNode(DBBase):
    __tablename__ = 'base_node'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class NodeTypeA(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeA'}
    typeB_children = relationship('NodeTypeB', backref='parent_node')


class NodeTypeB(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeB'}
    parent_id = Column(Integer, ForeignKey('base_node.id'))

使用这段代码会抛出错误:

sqlalchemy.exc.ArgumentError: NodeTypeA.typeB_children 和 back-reference NodeTypeB.parent_node 都是同一个方向的。 你是想在多对一的那一侧设置 remote_side 吗?

有什么想法或建议吗?

1 个回答

19

我之前也遇到过这个问题,后来我搞定了一个自引用的关系:

class Employee(Base):
  __tablename__ = 'employee'
  id = Column(Integer, primary_key=True)
  name = Column(String(64), nullable=False)
Employee.manager_id = Column(Integer, ForeignKey(Employee.id))
Employee.manager = relationship(Employee, backref='subordinates',
    remote_side=Employee.id)

注意,managermanager_id是“猴子补丁”,因为在类定义中不能直接进行自引用。

所以在你的例子中,我猜是这样的:

class NodeTypeA(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeA'}
    typeB_children = relationship('NodeTypeB', backref='parent_node',
        remote_side='NodeTypeB.parent_id')

编辑:基本上,你的错误提示是说这个关系和它的反向引用是一样的。所以,SA(SQLAlchemy)用来判断表级关系的规则和你提供的信息不一致。

我了解到,只要在你的声明类中写上mycolumn=relationship(OtherTable),就会让mycolumn变成一个列表,前提是SA能识别出一个明确的关系。所以,如果你真的想让一个对象链接到它的父对象,而不是子对象,你可以在子表中定义parent=relationship(OtherTable, backref='children', remote_side=OtherTable.id)。这样就定义了父子关系的两个方向。

撰写回答