2017-10-02 6 views
1

新しいマッピングをsession.mergeなどのデータベースデータとマージする方法はあるのですが、データベースを更新することはできませんか? gitでpullを実行したときと同様に、リモート状態と以前のローカル状態(未確定のコミットを含む可能性があります)のマージであるローカル状態を取得しますが、リモート状態は更新されません。ここでは、session.mergeを実行することによって生じる状態のローカルな「ビュー」を取得したいと考えています。SqlAlchemyでローカルの「マージ」

たぶん、その後session.mergeをやって、後session.rollbackでこれを実現するだろう、(session.begin_nested付き)セーブポイントを作り、実際には、DBの操作を元に戻す暗示することができますトランザクション管理のこの種を(必要としない方法があります、私の目的のために非常に効率的ではありません)?

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

マージは、自動フラッシュのためにデータベースを更新するだけです。 session.no_autoflushコンテキストマネージャを使用するか、セッションにautoflush=Falseを設定するだけで、一時的に無効にすることができます。 sessionmakerautoflush=Falseを渡すこともできます。

注意しなければならないことは、session.query(Model).all()の結果がフラッシュされていない、変更されたローカルオブジェクトとどのように対話するかです。

アイデンティティマップ内で一意のオブジェクト(プライマリキーに対して)のレコードを保持しているため、同一セッション内に同じオブジェクトの2つのバージョンを持つことはできません。ここで

は、ローカルの変更は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 

うん、それもno_autoflushで、データベースから元エドを取得することはできません - これは以来、get()のために予想されますそれはデータベースの前にIDマップを最初にチェックし、IDマップでIDマップを見つけたらDBを照会することを煩わしません。しかし、query.all()では、データベースを照会して、戻ってくるオブジェクトの1つがアイデンティティマップにすでに存在していて、代わりにその参照を返して、sessionのオブジェクトの一意性を維持するようにします(これは私の勘違いでした。これはドキュメントで明示的に綴られています)。

sessionからexpungeオブジェクトのようにすることができますが、私は、マージされたオブジェクトの古いものと新しいものを持つ最も簡単な方法は、2つの別々のセッションを使い、データベース内のオブジェクトの既存の状態をチェックするために使用することができます。

+0

Hmm、cool。それは理にかなっており、それが私が探している行動です。どうもありがとう! –

+0

クエリを実行して強制的にフラッシュするか、そうでなければ適切なバージョンのデータを公開しないかは、私が混乱しました。 –

関連する問題