проблема при фиксации данных с использованием объекта сеанса SQLAlchemy в цикле - PullRequest
3 голосов
/ 16 сентября 2011

У меня проблема с фиксацией базы данных с помощью SQLalchemy.Вот мой сценарий, у меня есть список записей, которые будут добавлены в таблицу.Как только записи добавлены в таблицу, мне нужно получить Id вставленных записей и сделать еще одну вставку во вторую таблицу.Я делаю эти операции в рамках одного сеанса.Структура кода приведена ниже, так как я не могу дать код

создает объект сеанса

цикл списка записей для вставки:

 do the session.add(obj)

 session.commit()

 get obj.id

 do the session.add(obj2) # with obj2 having the id from the obj

 session.commit()

только здесьданные последней записи корректно фиксируются, если у нас есть несколько записей

Может кто-нибудь помочь мне исправить эту проблему.

Ответы [ 2 ]

8 голосов
/ 17 сентября 2011

Очень велика вероятность того, что вам не нужно выполнить два коммита; Одна из главных причин использования сеансов sqlalchemy заключается в том, что он понимает, как объекты могут взаимодействовать, и упорядочивает вставки таким образом, чтобы данные вставлялись правильно и правильно представляли желаемую структуру. Это работает главным образом через конструкцию relationship. Вот простой пример:

>>> from sqlalchemy import *
>>> from sqlalchemy.orm import *
>>> from sqlalchemy.ext.declarative import declarative_base
>>> Base = declarative_base()

>>> class A(Base):
...     __tablename__ = "a_table"
...     id = Column(Integer, primary_key=True)
... 
>>> class B(Base):
...     __tablename__ = "b_table"
...     id = Column(Integer, primary_key=True)
...     a_id = Column(Integer, ForeignKey(A.id))
...     a = relationship(A)
... 
>>> my_a = A()
>>> my_b = B()
>>> my_b.a = my_a
>>> 

Самая важная часть в том, что мы объявляем отношения между A и B до B.a. Чтобы извлечь максимальную выгоду из этого, важно выразить взаимосвязь между экземплярами каждого через это свойство взаимосвязи, и пусть sqlalchemy позаботится об установке самого столбца a_id.

>>> engine = create_engine("sqlite:///:memory:")
>>> Base.metadata.create_all(engine)
>>> engine.echo = True
>>> Session = sessionmaker(engine)
>>> 
>>> session = Session()
>>> session.add(my_a)
>>> session.add(my_b)
>>> 
>>> session.commit()

При echo=True вывод выглядит примерно так:

2011-09-16 17:19:22,367 INFO sqlalchemy.engine.base.Engine.0x...ed50 BEGIN (implicit)
2011-09-16 17:19:22,368 INFO sqlalchemy.engine.base.Engine.0x...ed50 INSERT INTO a_table DEFAULT VALUES
2011-09-16 17:19:22,368 INFO sqlalchemy.engine.base.Engine.0x...ed50 ()
2011-09-16 17:19:22,369 INFO sqlalchemy.engine.base.Engine.0x...ed50 INSERT INTO b_table (a_id) VALUES (?)
2011-09-16 17:19:22,369 INFO sqlalchemy.engine.base.Engine.0x...ed50 (1,)
2011-09-16 17:19:22,369 INFO sqlalchemy.engine.base.Engine.0x...ed50 COMMIT

Обратите внимание, что объект my_a вставлен, и sqlalchemy читает назначенный первичный ключ и использует его для вставки для my_b.

4 голосов
/ 16 сентября 2011

Вы можете попробовать реструктурировать свой код, чтобы использовать это:

Session = sqlalchemy.orm.sessionmaker(...)
def transaction(self, callback):
  session = sqlalchemy.orm.scoped_session(Session)
  try:
    result = callback(session)
  except:
    session.rollback()
    raise
  else:
    session.commit()
  finally:
    session.close()
  return result

Тогда каждая транзакция выполняет свою функцию, например:

def updatetxn(pk, newvalue):
  def txn(session):
    obj = session.query(myclass).filter_by(id=pk).one()
    obj.field = newvalue
    session.add(obj)
  return txn

transaction(updatetxn(4, 'abc'))

Обработка логики фиксации / отката в одном месте и опора на область действия функции для охвата единицы работы могут снизить сложность вашего приложения и устранить ошибки, которые вы еще не нашли.

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