2009-12-04 11 views

Respuesta

37

SQLAlchemy está diseñado para tener un solo objeto con cada identidad en sesión. Pero a veces tiene que volver a crear un objeto con identidad conocida, p. cuando lo obtiene de la red o cuando implementa el bloqueo sin conexión para evitar transacciones largas. Y cuando crea un objeto con identidad conocida que pueda existir en la base de datos, existe la posibilidad de que la sesión ya rastree un objeto con esta identidad. Para eso sirve el método merge(): devuelve un objeto adjunto a la sesión, evitando así objetos duplicados con la misma identidad en la sesión. A continuación se muestra un ejemplo que ilustra lo que está pasando:

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 

La salida es:

True False 
value2 

Así session.merge(obj2) descubre que hay un objeto con la misma identidad (obj1 creado anteriormente), por lo que se funde la estado de obj2 en un objeto existente y lo devuelve.

A continuación se muestra otro ejemplo, que ilustran la carga de estado de la base de datos:

# ...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 

La salida es:

False False 
value1-2 value2-1 

Ahora merge() No ha encontrado el objeto con la misma identidad en la sesión, desde que lo borramos. También creé un nuevo objeto con estado parcialmente asignado, pero el resto se carga desde la base de datos.

+0

Gracias por una buena explicación, tenía algunas sombras sobre la idea detrás de _merge() _, ahora lo entiendo – Humoyun

10

Una cosa que noté acerca de la fusión es que incluso el acceso a un campo en el objeto no vinculado provocará que se modifique durante una fusión.

Por ejemplo, si tengo una clase simple

class X(Base): 
    __tablename__= 'x' 

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

y una fila

(1, 'foo', 'bar') 

continuación, la siguiente parece combinar muy bien:

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

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

Pero si incluso leer nombre o descripción, esto sucede

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

merged_x = session.merge(x) 

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

Esto es contrario a la intuición y, yo diría, incorrecto. ¿Hay alguna forma de desactivar este comportamiento? Aparentemente, la mera presencia de un atributo en __dict__ hace que esto suceda. Esta 'característica' debe anotarse en la documentación.

+0

No estoy seguro de estar de acuerdo con la edición @shane. ¿Puedes explicar? Lo que quise decir en el texto original fue que si accedía a 'x.y' y luego ejecutaba' session.merge (x) ',' 'x.y' no obtendría el valor fusionado. ¿Quiere decir que lo 'marcará' como modificado (y así evitará que se sobrescriba) en la fusión? –

+0

Sí, eso es exactamente lo que quise decir. Me encontré con el mismo problema, pero no estaba seguro sobre la forma en que fue redactado aquí. Tomé 'modificado' para significar 'persistió en la base de datos', ya que 'merged_x' ahora representa datos recientemente persistentes. Después de 'Session.merge (x)', los valores de atributos de X son modificados en absoluto (y no destinado a ser), 'merged_x' es un objeto nuevo. De – shane

+1

documentación: 'El estado de la instancia dada se copia en la instancia situado/recién creado. Para los atributos que están presentes en la instancia de origen, el valor se transfiere a la instancia de destino. Para los atributos asignados que no están presentes en la fuente, el atributo ha caducado en la instancia de destino, descartando su valor' existente – shane

Cuestiones relacionadas