SQLAlchemy 一对多关系澄清

Posted

技术标签:

【中文标题】SQLAlchemy 一对多关系澄清【英文标题】:SQLAlchemy one-to-many relationship clarification 【发布时间】:2016-03-16 02:15:07 【问题描述】:

阅读 SQLAlchemy 文档后,我仍然不清楚实际上应该如何指定一对多关系。我将分解文档并解释为什么我感到困惑(http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#one-to-many):

一对多关系在子表上放置一个外键 引用父级。

看起来我要在模型上放置一些 Column 属性,该属性将位于关系的“多”端。

relationship() 然后在父级上指定,作为引用由子级表示的项目集合:

这意味着父节点上有一些属性指定参与关系的“多”端的模型。

如果不是因为这样一种情况,我想定义两个一对多关系,关系双方的相同参与者,这对我来说完全有意义。

SQLAlchemy 如何知道关系“多”侧的ForeignKey 列对应于“一”侧的relationship 属性?

【问题讨论】:

【参考方案1】:

一对多的关系是这样建立的:

class Group(Base):
    id = Column(Integer, primary_key=True)
    users = relationship(lambda: User)

class User(Base):
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey(Group.id))

SQLAlchemy 推断您打算使用 parent_id 作为 users 的连接条件,因为它是链接两个表的唯一外键。

如果你有循环关系:

class Group(Base):
    id = Column(Integer, primary_key=True)
    owner_id = Column(Integer, ForeignKey("users.id"))
    users = relationship(lambda: User)

class User(Base):
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey(Group.id))
    owned_groups = relationship(Group)

如果你尝试这个,它不会起作用,因为 SQLAlchemy 抱怨它无法推断每个关系使用什么外键。相反,您必须明确告诉它使用什么:

class Group(Base):
    id = Column(Integer, primary_key=True)
    owner_id = Column(Integer, ForeignKey("users.id"))
    users = relationship(lambda: User, foreign_keys=lambda: User.parent_id)

class User(Base):
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey(Group.id))
    owned_groups = relationship(Group, foreign_keys=Group.owner_id)

一个更完整的反向引用示例:

class Group(Base):
    id = Column(Integer, primary_key=True)
    owner_id = Column(Integer, ForeignKey("users.id"))
    users = relationship(lambda: User, foreign_keys=lambda: User.parent_id, back_populates="parent")
    owner = relationship(lambda: User, foreign_keys=owner_id, back_populates="owned_groups")

class User(Base):
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey(Group.id))
    owned_groups = relationship(Group, foreign_keys=Group.owner_id, back_populates="owner")
    parent = relationship(Group, foreign_keys=parent_id, back_populates="users")

【讨论】:

感谢您的回答。看来您最近一直在回答我的大部分问题...感谢您的帮助! 有趣的是,SQLAlchemy 会对您的中间示例感到困惑,因为每个类上最多只有一个外键。 (你帮助回答了我的直接问题,所以我会按原样接受答案,但我很好奇你能否详细说明 SQLAlchemy 究竟会混淆什么。) @wheresmycookie SQLAlchemy 无法确定这一点的原因是,不能随意假设所有关系都是一对多的。它们也可以是多对一的。我添加了一个带有 backrefs 的示例,应该可以清楚地说明这一点。在每一侧,实际上有两个关系对应于两个外键中的每一个。想象一下你是 SQLAlchemy。在第二个例子中,你怎么知道owned_groups 不应该是users 关系的另一端(因此使用parent_id 外键)?

以上是关于SQLAlchemy 一对多关系澄清的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy_定义(一对一/一对多/多对多)关系

在sqlalchemy中插入具有一对多关系的新记录

Flask 学习-78.Flask-SQLAlchemy 一对多关系

sqlalchemy一对多的关系

SQLAlchemy:直接从一对多关系中删除对象,而不使用 session.delete()

Flask-SQLAlchemy 配置,处理对象-关系,一对多,多对多