【问题标题】:One to many + one relationship in SQLAlchemy?SQLAlchemy中的一对多+一关系?
【发布时间】:2016-08-22 22:29:12
【问题描述】:

我试图模拟以下情况:一个程序有多个版本,其中一个版本是当前版本(不一定是最新版本)。

这就是我现在的做法:

class Program(Base):
    __tablename__ = 'programs'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    current_version_id = Column(Integer, ForeignKey('program_versions.id'))

    current_version = relationship('ProgramVersion', foreign_keys=[current_version_id])
    versions = relationship('ProgramVersion', order_by='ProgramVersion.id', back_populates='program')


class ProgramVersion(Base):
    __tablename__ = 'program_versions'
    id = Column(Integer, primary_key=True)
    program_id = Column(Integer, ForeignKey('programs.id'))
    timestamp = Column(DateTime, default=datetime.datetime.utcnow)

    program = relationship('Filter', foreign_keys=[program_id], back_populates='versions')

然后我收到错误:无法确定关系 Program.versions 上父/子表之间的连接条件 - 有多个外键路径链接表。指定 'foreign_keys' 参数,提供应计为包含对父表的外键引用的列的列表。

但是我应该为“Program.versions”关系提供什么外键?有没有更好的方法来模拟这种情况?

【问题讨论】:

    标签: python database sqlalchemy


    【解决方案1】:

    像这样的循环依赖是解决这个问题的完美方法。

    要解决您的外键问题,您需要明确提供 foreign_keys 参数。

    class Program(Base):
        ...
        current_version = relationship('ProgramVersion', foreign_keys=current_version_id, ...)
        versions = relationship('ProgramVersion', foreign_keys="ProgramVersion.program_id", ...)
    
    class ProgramVersion(Base):
        ...
        program = relationship('Filter', foreign_keys=program_id, ...)
    

    您会发现,当您执行create_all() 时,SQLAlchemy 无法创建表,因为每个表都有一个依赖于另一个列的外键。 SQLAlchemy 提供了一种方法来打破这种循环依赖关系,方法是对其中一个表使用 ALTER 语句:

    class Program(Base):
        ...
        current_version_id = Column(Integer, ForeignKey('program_versions.id', use_alter=True, name="fk_program_current_version_id"))
       ...
    

    最后,您会发现,当您向会话中添加完整的对象图时,SQLAlchemy 无法发出INSERT 语句,因为每一行的值取决于另一行的未知主键。 SQLAlchemy 提供了一种方法来打破这种循环依赖,方法是为其中一个列发出 UPDATE

    class Program(Base):
        ...
        current_version = relationship('ProgramVersion', foreign_keys=current_version_id, post_update=True, ...)
        ...
    

    【讨论】:

      【解决方案2】:

      这种设计并不理想;通过让两个表相互引用,您无法有效地插入其中一个表,因为另一个表所需的外键将不存在。选择的答案中概述了一种可能的解决方案 this question related to microsoft sqlserver,但我会在这里总结/详细说明。

      对此进行建模的更好方法可能是引入第三个表 VersionHistory,并消除对其他两个表的外键约束。

      class VersionHistory(Base):
          __tablename__ = 'version_history'
          program_id = Column(Integer, ForeignKey('programs.id'), primary_key=True)
          version_id = Column(Integer, ForeignKey('program_version.id'), primary_key=True)
          current = Column(Boolean, default=False)
          # I'm not too familiar with SQLAlchemy, but I suspect that relationship 
          # information goes here somewhere
      

      这消除了您在当前实现中创建的循环关系。然后,您可以按程序查询此表,并接收该程序的所有现有版本等。由于此表中的复合主键,您可以访问任何特定的程序/版本组合。在此表中添加 current 字段可以减轻其他两个表中跟踪货币的负担,尽管为每个程序维护一个当前版本可能需要一些触发体操。

      HTH!

      【讨论】:

        猜你喜欢
        • 2019-12-06
        • 2020-11-01
        • 2016-06-15
        • 2021-08-31
        • 1970-01-01
        • 2021-09-07
        • 1970-01-01
        • 2014-05-26
        • 1970-01-01
        相关资源
        最近更新 更多