SqlAlchemy中的本地“合并”

时间:2017-10-02 23:07:40

标签: python database merge sqlalchemy

我想知道是否有办法将新映射与数据库数据合并,例如与session.merge合并,但是没有更新数据库?就像我使用git执行pull一样,获取一个本地状态,它是远程和先前本地状态的合并(可能包含未提交的提交),但不更新远程状态。在这里,我希望获得一个本地“视图”状态,这可以通过执行session.merge来实现。

也许制作保存点(使用session.begin_nested),然后执行session.merge以及稍后session.rollback就可以完成此操作,但是有一种方法不需要这种交易管理(这可能意味着对数据库进行实际的撤消操作,因此对我的目的来说效率不是很高)?

使用session.no_autoflush会不会这样做?

我想要做的示例代码:

local_merge = session.merge_local(Model(...))
# do stuff with local_merge...
remotes = session.query(Model).all()
# remotes should remain "old" db versions, as no data was pushed
return something 

编辑:所以我认为rollback方法效率低下我可能错了。至少,只要没有发出commit,就不应该进行昂贵的撤销操作,只能清除事务。

1 个答案:

答案 0 :(得分:1)

Merge只会因为自动刷新而更新数据库。您可以使用session.no_autoflush上下文管理器临时关闭该功能,或者只在会话中设置autoflush=False。您也可以将autoflush=False传递给sessionmaker

需要注意的一件事是session.query(Model).all()的结果将如何与未刷新的,已更改的本地对象进行交互。

由于session在身份地图中维护了唯一对象(针对主键)的记录,因此您无法在同一会话中拥有同一对象的两个版本。

以下是显示本地更改如何与autoflush=False互动的示例:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()


class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)

    def __repr__(self):
        return "<User(name='%s')>" % self.name


Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

ed_user = User(name='ed')
session.add(ed_user)
session.commit()

ed_again = session.query(User).get(1)
ed_again.name = "not ed"

with session.no_autoflush:
    ed_three = session.query(User).get(1)
    all_eds = session.query(User).all()
    print(ed_again, id(ed_again))
    print(ed_three, id(ed_three))
    print(all_eds, id(all_eds[0]))

<User(name='not ed')> 139664151068624
<User(name='not ed')> 139664151068624
[<User(name='not ed')>] 139664151068624

是的,它无法从数据库中获取原始Ed,即使使用no_autoflush - get()也是如此,因为它会先检查身份地图数据库,如果它在身份图中找到它,就不会打扰查询数据库。但是对于query.all(),它查询数据库,发现它获取的其中一个对象已经在身份映射中,并返回该引用,以便保持session中的对象的唯一性(也是我的预感,但我无法在文档中明确说明这一点。)

您可以执行sessionexpunge个对象的操作,但我认为拥有合并对象的旧版本和新版本的最简单方法是使用两个单独的会话,其中一个是更改将被合并并可能提交,您可以使用它来检查数据库中对象的现有状态。