2017-01-09 11 views
0

を削除し、その子に、それに対するすべての参照を変更した後、親を削除: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

答えて

0

私は削除操作を行う前に変更をコミットしようとします。そうでなければ、私は削除がそれの前に直接コミット操作を認識していないことを推測している:

def merge_1(session, src_prj, dst_prj): 
    for task in src_prj.tasks: 
     task.project = dst_prj 
    session.commit() 
    session.delete(src_prj) 

あなたはUPDATE文ではなく、個別にそれを行うことができますか?何かのように:

Project.objects.filter(name=src_prj).update(name=dst_prj) 
Project.objects.filter(name=src_prj).delete() 
+0

これは私が 'merge_3'で行ったことです。それはうまくいくが、原子の変化はないのだろうか?何らかの理由でdelete()呼び出しが失敗した場合、タスクプロジェクトの変更をロールバックすることはできません。 –

+0

@agateauは正しいです。最も良い方法は、2番目のメソッドを実行し、更新と削除の両方が完了した後にcommitを呼び出すことです。最初のものを原子的に行うには、 'src_prj.tasks'以外の場所から'タスク 'を取得する必要があります。なぜなら、これは削除されるものなので。 – David542