所以我在没有太多背景的情况下提出了这个问题,然后投了反对,让我们再试一次......
首先,我不遵循SQLAlchemy session.add
背后的逻辑。我理解它将对象排队插入,我理解session.query
在连接的数据库中而不是在会话中查找,但在SQLAlchemy中,它可以在不首先查询会话的情况下查询会话{{1} }}?我对来自session.flush
的内容的期望是它会查询会话...
我现在在session.query
session.new
出来之后手动查看None
。
在我的session.query().first()
之前,我不想做session.flush
的两个原因,
session.query
,其实例导致flush也提交。所以这个问题的核心是谁帮助我在github上的GPL程序中找到错误!
这是一个在bauble / ghini中出现令人惊讶的行为的代码片段:
Session
这里的输出是:
# setting up things in ghini
# <replace-later>
import bauble
import bauble.db as db
db.open('sqlite:///:memory:', verify=False)
from bauble.prefs import prefs
import bauble.pluginmgr as pluginmgr
prefs.init()
prefs.testing = True
pluginmgr.load()
db.create(True)
Session = bauble.db.Session
from bauble.plugins.garden import Location
# </replace-later>
# now just plain straightforward usage
session = Session()
session.query(Location).delete()
session.commit()
u0 = session.query(Location).filter_by(code=u'mario').first()
print u0
u1 = Location(code=u'mario')
session.add(u1)
session.flush()
u2 = session.query(Location).filter_by(code=u'mario').one()
print u1, u2, u1==u2
session.rollback()
u3 = session.query(Location).filter_by(code=u'mario').first()
print u3
这里你有我认为只是标准的简单代码来设置数据库:
None
mario mario True
mario
有了这个,上面相同代码片段的输出就不那么令人惊讶了:
from sqlalchemy import Column, Unicode
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Location(Base):
__tablename__ = 'location'
code = Column(Unicode(64), index=True, primary_key=True)
def __init__(self, code=None):
self.code = code
def __repr__(self):
return self.code
from sqlalchemy import create_engine
engine = create_engine('sqlite:///joindemo.db')
Base.metadata.create_all(engine)
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine, autoflush=False)
答案 0 :(得分:2)
bauble中的刷新最终发出COMMIT的原因是line 133 in db.py
处理历史记录表的原因:
table.insert(dict(table_name=mapper.local_table.name,
table_id=instance.id, values=str(row),
operation=operation, user=user,
timestamp=datetime.datetime.today())).execute()
不是使用传入的事务连接在事件处理程序中发出附加SQL,而是,因为它们应该,而是按原样执行语句,这意味着它最终使用引擎作为绑定(通过表格的元数据找到)。使用引擎执行具有autocommit行为。由于bauble always uses一个SingletonThreadPool
,每个线程只有一个连接,因此该语句最终也会提交刷新的更改。我想知道这个错误是否是为什么bauble禁用autoflush ...
修复方法是更改事件处理以使用事务连接:
class HistoryExtension(orm.MapperExtension):
"""
HistoryExtension is a
:class:`~sqlalchemy.orm.interfaces.MapperExtension` that is added
to all clases that inherit from bauble.db.Base so that all
inserts, updates, and deletes made to the mapped objects are
recorded in the `history` table.
"""
def _add(self, operation, mapper, connection, instance):
"""
Add a new entry to the history table.
"""
... # a ton of code here
table = History.__table__
stmt = table.insert(dict(table_name=mapper.local_table.name,
table_id=instance.id, values=str(row),
operation=operation, user=user,
timestamp=datetime.datetime.today()))
connection.execute(stmt)
def after_update(self, mapper, connection, instance):
self._add('update', mapper, connection, instance)
def after_insert(self, mapper, connection, instance):
self._add('insert', mapper, connection, instance)
def after_delete(self, mapper, connection, instance):
self._add('delete', mapper, connection, instance)
值得注意的是,自版本0.7以来MapperExtension
已被弃用。
关于你对会话的看法,我引用了"Session Basics",你真正应该阅读:
在最一般意义上,
Session
建立与数据库的所有对话,并代表您在其生命周期内加载或与之关联的所有对象的“保留区域”。它提供了获取Query
对象的入口点,该对象使用Session
对象的当前数据库连接向数据库发送查询,...
... Yeee没有。它有点用作缓存,因为它实现了身份映射模式,并存储了键入其主键的对象。但是,它不执行任何类型的查询缓存。这意味着,如果你说
session.query(Foo).filter_by(name='bar')
,即使Foo(name='bar')
就在那里,在身份地图中,会话也不知道。它必须向数据库发出SQL ,获取行,然后当它看到行中的主键时,它可以查看本地身份映射并查看该对象已经存在。只有当您query.get({some primary key})
说Session
不必发出查询时,才会这样做。
所以:
我期望读取session.query的内容是它查询会话...
你的期望是错误的。 Session
处理与数据库的对话 - 在other things中。
在我的会话之前,我不想做session.flush的两个原因。查询,
- 一个基于效率恐惧的问题(为什么我应该写入数据库,如果我仍然在用户可能想要回滚的会话中,则查询数据库?);
因为您的数据库可能会进行验证,拥有触发器并为某些列生成值 - 主键,时间戳等。您认为自己要插入的数据可能会导致数据库中出现其他内容,而Session
绝对无法知道这一点。
此外,为什么SQLAlchemy本身应该使用自己的查询引擎实现一种内存数据库,以及同步2个数据库所带来的所有问题? SQLAlchemy如何支持您查询的不同数据库的所有不同操作和功能?您的简单等式谓词示例只是略微浮出水面。
当您回滚时,您将回滚数据库的事务(以及会话的未刷新更改)。
- 两个是因为我已经采用了一个相当大的程序,它设法定义了自己的Session,其实例导致flush也提交。
由事件处理错误引起。