2016-08-08 16 views
1

私は次のような状況をモデル化しようとしています。プログラムには多くのバージョンがあり、そのうちの1つは現在のものです(必ずしも最新ではありません)。SQLAlchemyの1対1の関係?

これは私が今それをやっている方法です:

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'関係にはどのような外部キーを用意する必要がありますか?この状況をモデル化する良い方法はありますか?

答えて

0

このデザインは理想的ではありません。 2つのテーブルを相互に参照することによって、一方のテーブルに必要な外部キーが存在しないため、どちらのテーブルにも効果的に挿入できません。選択された答えの1つの可能な解決策は this question related to microsoft sqlserverですが、ここでは要約します。

これをモデル化するより良い方法は、3番目の表VersionHistoryを導入し、他の2つの表の外部キー制約を取り除くことです。

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フィールドをこのテーブルに追加すると、他の2つのテーブルから通貨を追跡する負担がかかりますが、プログラムごとに1つの現在のバージョンを維持すると、トリガーの体操が必要になることがあります。

HTH!

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は、列の1つにUPDATEを発行することによって、この循環依存関係を解消する方法を提供します。

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