代码之家  ›  专栏  ›  技术社区  ›  joeforker

sqlAlchemy的session.merge()是否可以用数据库中更新的数据更新其结果?

  •  13
  • joeforker  · 技术社区  · 15 年前

    sqlAlchemy文档说 session.merge() 协调实例及其关联子级的当前状态与数据库中的现有数据”。

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

    2 回复  |  直到 8 年前
        1
  •  43
  •   Denis Otkidach    15 年前

    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
    

    输出是:

    True False
    value2
    

    因此 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
    

    输出是:

    False False
    value1-2 value2-1
    

    现在 () 在会话中找不到具有相同标识的对象,因为我们删除了它。另外,我创建了一个状态部分分配的新对象,但其余的都是从数据库加载的。

        2
  •  11
  •   shane    8 年前

    关于合并,我注意到的一件事是,即使访问未连接对象上的字段,也会导致在合并期间对其进行修改。

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

    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'
    

    这是违反直觉的,我认为是不正确的。有没有办法阻止这种行为?显然,仅仅在口述中存在一个属性就导致了这种情况的发生。应在文档中注明此“功能”。