代码之家  ›  专栏  ›  技术社区  ›  Charles Langlois

SqlAlchemy中的局部“合并”

  •  1
  • Charles Langlois  · 技术社区  · 8 年前

    session.merge ,但不更新数据库?比如当我做一个 pull 使用git,获取本地状态,该状态是远程状态和以前的本地状态(可能包含未推送的提交)的合并,但不更新远程状态。在这里,我想得到一个地方的“看法”的状态,这将导致从做一个 .

    session.begin_nested ),然后执行 之后 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 回复  |  直到 8 年前
        1
  •  2
  •   Rach Sharp    8 年前

    由于自动刷新,Merge将仅更新数据库。您可以使用 session.no_autoflush 上下文管理器,或只是设置 autoflush=False 在您的会话中。你也可以通过 自动刷新=假 sessionmaker

    需要注意的一件事是 session.query(Model).all()

    因为 session 在标识图中维护唯一对象的记录(相对于主键),您将无法在同一会话中拥有同一对象的两个版本。

    下面是一个示例,展示了本地更改如何与 :

    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
    

    no_autoflush -这是意料之中的 get() ,因为它将在数据库之前首先检查身份映射,如果在身份映射中找到数据库,则不会费事查询数据库。但是有 query.all() 一场 (这也是我的直觉,但我在文档中找不到明确的解释)。

    expunge 来自 一场 ,但我认为获得合并对象的新旧副本的最简单方法是使用两个单独的会话,一个会话将合并并可能提交更改,另一个会话可以用于检查数据库中对象的现有状态。