を削除し、その子に、それに対するすべての参照を変更した後、親を削除:SQLAlchemyのはまだこのSQLAlchemyのデータベース定義を考えると、子供
class Project(Base):
__tablename__ = 'project'
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True)
tasks = relationship('Task', cascade='all', backref='project')
class Task(Base):
__tablename__ = 'task'
id = Column(Integer, primary_key=True)
title = Column(Unicode)
project_id = Column(Integer, ForeignKey('project.id'), nullable=False)
は、私は2つのプロジェクトをマージしたいです。私の最初の素朴な試みは、このような何かをすることでした。(!)
def merge_1(session, src_prj, dst_prj):
for task in src_prj.tasks:
task.project = dst_prj
session.delete(src_prj)
しかし、唯一の半分の原因となったことで転送されるタスクの、他の半分は削除されました。
代わりに私がこれを行う場合は、次の私の仕事の
def merge_2(session, src_prj, dst_prj):
for task in src_prj.tasks:
task.project_id = dst_prj.id
session.delete(src_prj)
なしで転送されていませんが。プロジェクトが削除されると削除されます。
def merge_3(session, src_prj, dst_prj):
for task in src_prj.tasks:
task.project_id = dst_prj.id
session.commit()
session.delete(src_prj)
それは動作しますが、プロジェクトを削除する前にsession.commit()
を呼び出すと、セッション取引の目的に反し:
この最終バージョンでは、同様に動作します(と高速です):
def merge_4(session, src_prj, dst_prj):
session.query(Task).filter_by(project_id=src_prj.id) \
.update({'project_id': dst_prj.id})
session.delete(src_prj)
しかし、私は予想通りmerge_1()
とmerge_2()
は動作しません理由を知りたいと思います。
私はSQLAlchemy 1.1.4を使用してテストしました。完全なテストプログラムはここにあります:https://gist.github.com/agateau/887af14b7ddd1e151f9ac89d5e423ef6
これは私が 'merge_3'で行ったことです。それはうまくいくが、原子の変化はないのだろうか?何らかの理由でdelete()呼び出しが失敗した場合、タスクプロジェクトの変更をロールバックすることはできません。 –
@agateauは正しいです。最も良い方法は、2番目のメソッドを実行し、更新と削除の両方が完了した後にcommitを呼び出すことです。最初のものを原子的に行うには、 'src_prj.tasks'以外の場所から'タスク 'を取得する必要があります。なぜなら、これは削除されるものなので。 – David542