私はSQLAlchemyリリース0.8.2を使用しています(python 2.7.5および3.3.2を試しました)
コードで関連付けオブジェクト パターン (多対多の関係用) を使用する必要がありましたが、関連付けを追加するたびに IntegrityError 例外が発生していました。これは、"INSERT INTO association (left_id, right_id, extra_data) [...]" を実行する代わりに、"INSERT INTO association (right_id, extra_data) [...]" を実行し、IntegrityError が発生するためです。主キーがないため例外です。
しばらく問題を絞り込み、コードをできるだけ単純化しようとした後、犯人を見つけましたが、なぜこのように動作するのかわかりません。
読者がそのままテストできるように、完全なコードを含めました。クラス宣言は、ドキュメントとまったく同じです (後方参照あり)。
#!/usr/bin/env python2
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
Base = declarative_base()
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", backref="parent_assocs")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", backref="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
def main():
engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# populate old data
session.add(Child())
# new data
p = Parent()
session.add(p) # Commenting this fixes the error.
session.flush()
# rest of new data
a = Association(extra_data="some data")
a.child = session.query(Child).one()
# a.child = Child() # Using this instead of the above line avoids the error - but that's not what I want.
p.children.append(a)
# a.parent = p # Using this instead of the above line fixes the error! They're logically equivalent.
session.add(p)
session.commit()
if __name__ == '__main__':
main()
したがって、上記のコードのコメントで述べたように、問題を修正/回避するには 3 つの方法があります。
- 関連付けを宣言する前に親をセッションに追加しないでください
- 既存の子を選択する代わりに、関連付けの新しい子を作成します。
- 関連付けで後方参照を使用する
3 つのケースすべての動作がわかりません。
2 番目のケースは別のことを行うため、可能な解決策ではありません。ただし、動作がわかりません。この場合、問題が回避される理由の説明をいただければ幸いです。
最初のケースは「オブジェクトの状態」と関係があるのではないかと考えていますが、何が原因なのか正確にはわかりません。ああ、session.autoflush=False
最初に発生する直前にsession.add(p)
追加すると、混乱を招く問題も修正されます。
3 番目のケースでは、論理的に同等である必要があるため、完全な空白を描いています。
洞察をありがとう!