Может ли session.merge () SQLAlchemy обновить свой результат новыми данными из базы данных? - PullRequest
14 голосов
/ 04 декабря 2009

Документация SQLAlchemy гласит: " session.merge () согласовывает текущее состояние экземпляра и связанных с ним дочерних элементов с существующими данными в базе данных".

Обновляется ли когда-либо состояние существующего объекта новыми данными из базы данных? Как? Когда?

Ответы [ 2 ]

46 голосов
/ 05 декабря 2009

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

Теперь merge() не нашел объект с таким же идентификатором в сеансе, так как мы удалили его. Также я создал новый объект с частично присвоенным состоянием, но остальное загружается из базы данных.

11 голосов
/ 09 сентября 2013

Одна вещь, которую я заметил в слиянии, это то, что даже доступ к полю на неприкрепленном объекте приведет к его изменению во время слияния.

Например, если у меня есть простой класс

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__ приводит к тому, что это происходит. Эта «особенность» должна быть отмечена в документации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...