我试图模拟以下情况:程序有很多版本,其中一个版本是当前版本(不一定是最新版本)。
这就是我现在这样做的方式:
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'参数,提供应列为包含父表的外键引用的列的列表。
但是,我应该为“计划”提供什么外键?'关系?有没有更好的方法来模拟这种情况?
答案 0 :(得分:0)
这种设计并不理想;通过让两个表相互引用,您无法有效地插入到任一个表中,因为另一个表中所需的外键将不存在。在所选答案中概述的一种可能的解决方案 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!
答案 1 :(得分:0)
这样的循环依赖是解决这个问题的完美有效方法。
要修复外键问题,您需要显式提供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, ...)
...