python - トランザクション - sqlalchemy update 複数



SQLAlchemyのsession.merge()はデータベースの新しいデータで結果を更新できますか? (2)

SQLAlchemyのドキュメントには、 " session.merge()はインスタンスとその関連する子の現在の状態をデータベース内の既存のデータとsession.merge()せる"と書かれています。

既存のオブジェクトの状態は、データベースからの新しいデータによって更新されることがありますか? どうやって? いつ?


Answer #1

SQLAlchemyは、セッション内の各IDを持つ単一のオブジェクトを持つように設計されています。 しかし、ネットワークから取得したときや、長いトランザクションを避けるためにオフラインロックを実装したときなど、既知のIDを持つオブジェクトを再作成する必要がある場合があります。 データベースに存在する可能性がある既知のIDを持つオブジェクトを作成すると、セッションがすでにこのIDを持つオブジェクトを追跡している可能性があります。 それmerge()メソッドの目的です。セッションにアタッチされているオブジェクトを返すため、セッション内で同じIDを持つオブジェクトが重複することはありません。 以下は、何が起こっているのかを示す例です。

from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData()

t = Table(
    't', metadata,
    Column('id', Integer, primary_key=True),
    Column('state', String(10)),
)

class Model(object): pass

mapper(Model, t)

engine = create_engine('sqlite://')
metadata.create_all(engine)

session = sessionmaker(bind=engine)()

obj1 = Model()
obj1.state = 'value1'
session.add(obj1)
session.commit()
obj_id = obj1.id

obj2 = Model()
obj2.id = obj_id
obj2.state = 'value2'
obj3 = session.merge(obj2)
session.commit()
print obj3 is obj1, obj3 is obj2
print obj3.state

出力は以下のとおりです。

True False
value2

したがって、 session.merge(obj2)は、同じIDを持つオブジェクト(上記で作成したobj1 )があることを検出したため、 obj2の状態を既存のオブジェクトにマージして返します。

以下はデータベースからの状態のロードを説明する別の例です。

# ...skipped...

t = Table(
    't', metadata,
    Column('id', Integer, primary_key=True),
    Column('state1', String(10)),
    Column('state2', String(10)),
)

# ...skipped...

obj1 = Model()
obj1.state1 = 'value1-1'
obj1.state2 = 'value2-1'
session.add(obj1)
session.commit()
obj_id = obj1.id
session.expunge_all()

obj2 = Model()
obj2.id = obj_id
obj2.state1 = 'value1-2'
obj3 = session.merge(obj2)
session.commit()
print obj3 is obj1, obj3 is obj2
print obj3.state1, obj3.state2

出力は以下のとおりです。

False False
value1-2 value2-1

これで、 merge()は、消去したため、セッション内で同じIDを持つオブジェクトを見つけられませんでした。 また、部分的に割り当てられた状態で新しいオブジェクトを作成しましたが、残りはデータベースからロードされます。


Answer #2

私がマージについて気づいたことの1つは、アタッチされていないオブジェクトのフィールドにアクセスしても、マージ中にフィールドが変更されることです。

たとえば、単純なクラスがあるとします。

class X(Base):
    __tablename__= 'x'

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

そして行

(1, 'foo', 'bar')

それでは、以下はうまく結合しているようです。

x = X(id=1)
merged_x = session.merge(x)

print merged_x.name         # 'foo'
print merged_x.description  # 'bar'

しかし、名前や説明を読んでも、これは起こります

x = X(id=1)
print x.name                # None

merged_x = session.merge(x)

print merged_x.name         # None
print merged_x.description  # 'bar'

これは直感に反するものです。 この動作を無効にする方法はありますか? どうやら__dict__に属性が存在するだけでこれが起こります。 この「機能」はドキュメントに記載されています。





sqlalchemy