Может ли session.merge() SQLAlchemy обновить свой результат новыми данными из базы данных?
-
13-09-2019 - |
Вопрос
В документации SQLAlchemy говорится : "сессия.слияние() согласовывает текущее состояние экземпляра и связанных с ним дочерних элементов с существующими данными в базе данных".
Обновляется ли когда-нибудь состояние существующего объекта новыми данными из базы данных?Каким образом?Когда?
Решение
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()
не удалось найти объект с таким же идентификатором в сеансе, так как мы удалили его.Также я создал новый объект с частично присвоенным состоянием, но остальное загружено из базы данных.
Другие советы
Одна вещь, которую я заметил в отношении слияния, заключается в том, что даже доступ к полю в несвязанном объекте приведет к его изменению во время слияния.
Например, если у меня есть простой класс
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__ приводит к тому, что это происходит.Эта "функция" должна быть отмечена в документации.