SQLAlchemy的session.merge()可以使用数据库中的较新数据更新其结果吗?

joe*_*ker 15 python sqlalchemy

SQLAlchemy文档说" session.merge()将实例及其关联子项的当前状态与数据库中的现有数据进行协调".

现有对象的状态是否会被数据库中的较新数据更新?怎么样?什么时候?

Den*_*ach 50

SQLAlchemy旨在为会话中的每个标识提供单个对象.但有时您必须重新创建具有已知标识的对象,例如,当您从网络获取它或实现脱机锁定以避免长事务时.当您创建可能存在于数据库中的已知标识的对象时,会话可能已经跟踪具有此标识的对象.这merge()就是用于的方法:它返回附加到会话的对象,从而避免会话中具有相同标识的重复对象.下面是一个说明正在发生的事情的示例:

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
Run Code Online (Sandbox Code Playgroud)

输出是:

True False
value2
Run Code Online (Sandbox Code Playgroud)

因此session.merge(obj2)发现存在具有相同标识的对象(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
Run Code Online (Sandbox Code Playgroud)

输出是:

False False
value1-2 value2-1
Run Code Online (Sandbox Code Playgroud)

现在merge()没有在会话中找到具有相同身份的对象,因为我们已经删除了它.我还创建了具有部分分配状态的新对象,但其余部分是从数据库加载的.


Ada*_*hue 11

我注意到有关合并的一件事是,即使访问未附加对象上的字段也会导致在合并期间修改它.

例如,如果我有一个简单的类

class X(Base):
    __tablename__= 'x'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    value = Column(String)
Run Code Online (Sandbox Code Playgroud)

和一排

(1, 'foo', 'bar')
Run Code Online (Sandbox Code Playgroud)

然后以下似乎合并得很好:

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

print merged_x.name         # 'foo'
print merged_x.description  # 'bar'
Run Code Online (Sandbox Code Playgroud)

但如果我甚至阅读名称或描述,就会发生这种情况

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

merged_x = session.merge(x)

print merged_x.name         # None
print merged_x.description  # 'bar'
Run Code Online (Sandbox Code Playgroud)

这是违反直觉的,我认为,这是不正确的.有没有办法关闭这种行为?显然,在__dict__中只存在一个属性会导致这种情况发生.应在文档中注明此"功能".