鉴于此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)
我想合并两个项目。我的第一个天真的尝试是做这样的事情:
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 :(得分: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)
您可以在更新声明中而不是单独执行此操作吗?类似的东西:
Project.objects.filter(name=src_prj).update(name=dst_prj)
Project.objects.filter(name=src_prj).delete()