在循环中使用SQLAlchemy Session对象提交数据时出现问题
我在使用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
声明了A
和B
之间的关系。为了充分利用这一点,重要的是要通过这个关系属性来表达每个实例之间的关系,让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'))
把提交和回滚的逻辑放在一个地方处理,并利用函数的作用域来管理一项工作的范围,这样可以减少你应用程序的复杂性,还能消除一些你可能还没发现的错误。