在循环中使用SQLAlchemy Session对象提交数据时出现问题

3 投票
2 回答
4457 浏览
提问于 2025-04-17 02:27

我在使用SQLalchemy时遇到了数据库提交的问题。我的情况是,我有一堆记录需要添加到一个表里。每当我把一条记录添加到表中时,我需要获取这条记录的ID,然后再把它插入到第二个表里。我是在一个会话(session)中进行这些操作的。下面是代码结构,但我不能提供具体的代码。

首先,创建一个会话对象。

然后,循环遍历要插入的记录列表:

 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

你很可能不需要进行两次提交。使用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
>>> 

最重要的是,我们通过B.a声明了AB之间的关系。为了充分利用这一点,重要的是要通过这个关系属性来表达每个实例之间的关系,让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

你可以试着重新组织一下你的代码,使用这个:

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'))

把提交和回滚的逻辑放在一个地方处理,并利用函数的作用域来管理一项工作的范围,这样可以减少你应用程序的复杂性,还能消除一些你可能还没发现的错误。

撰写回答