Python 在SQLAlchemy中查询添加到未提交会话的对象

Python 在SQLAlchemy中查询添加到未提交会话的对象,python,transactions,sqlalchemy,Python,Transactions,Sqlalchemy,所以我在没有太多背景的情况下提出了这个问题,结果被否决了,让我们再试一次 首先,我没有遵循SQLAlchemy的会话.add的逻辑。我知道它将对象排队等待插入,我也知道session.query在连接的数据库中而不是在会话中查找,但是在SQLAlchemy中,是否有可能在不首先执行session.flush的情况下查询会话?我对session.query的期望是它查询session.query 我现在正在手动查看会话。在会话中出现无后新建。query().first() 我不想执行会话的原因有

所以我在没有太多背景的情况下提出了这个问题,结果被否决了,让我们再试一次

首先,我没有遵循SQLAlchemy的
会话.add的逻辑。我知道它将对象排队等待插入,我也知道
session.query
在连接的数据库中而不是在会话中查找,但是在SQLAlchemy中,是否有可能在不首先执行
session.flush
的情况下查询会话?我对session.query的期望是它查询session.query

我现在正在手动查看
会话。在
会话中出现
后新建
。query().first()


我不想执行
会话的原因有两个。在执行
会话之前刷新
。查询

  • 一个是基于效率的担忧(如果我仍在用户可能希望回滚的会话中,为什么我要写入数据库并查询数据库?)
  • 第二个原因是我采用了一个,它设法定义了自己的
    会话
    ,其实例导致flush也提交
所以这个问题的核心是谁在帮助我在github上发现GPL程序中的错误

这是一段代码片段,在bauble/ghini中有一个令人惊讶的行为:

# 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
这里有我认为只是建立数据库的标准简单代码:

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)
这样,上述相同代码片段的输出就不那么令人惊讶了:

None
mario mario True
None

bauble中的刷新最终发出提交的原因是它们处理历史记录表的位置:

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,而是按原样执行语句本身,这意味着它最终使用引擎作为绑定(通过表的元数据找到)。使用引擎执行具有以下行为。由于a,每个线程只有一个连接,因此该语句最终也会提交刷新的更改。我想知道这个错误是否是bauble禁用

修复方法是将事件处理更改为使用事务连接:

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版以来,它一直被弃用


关于你对我引用的会议的看法,你确实应该仔细阅读:

从最一般的意义上讲,
会话
建立了与数据库的所有对话,并为您在其生命周期内加载或关联的所有对象表示一个“保留区”。它提供入口点以获取
查询
对象,该对象使用
会话
对象的当前数据库连接将查询发送到数据库

以及:

Yeee…不是。它在某种程度上被用作缓存,因为它实现了身份映射模式,并存储键控到主键的对象。但是,它不做任何类型的查询缓存。这意味着,如果你说
session.query(Foo).filter\u by(name='bar')
,即使
Foo(name='bar')
就在那里,在身份映射中,会话对此一无所知。它必须向数据库发出SQL,取回行,然后当它看到行中的主键时,它可以在本地标识映射中查看对象是否已经存在。只有当您说
query.get({some primary key})
时,
会话才不必发出查询

因此:

我对session.query的期望是它查询会话

你的期望是错误的。
会话
处理与数据库之间的对话

我不想在session.query之前执行session.flush有两个原因

  • 一个是基于效率的担忧(如果我仍在用户可能希望回滚的会话中,为什么我要写入数据库并查询数据库?)
因为您的数据库可能会进行验证,具有触发器,并为某些列生成值—主键、时间戳等。您认为要插入的数据可能会在数据库中出现其他内容,
会话
完全无法知道这一点

另外,为什么SQLAlchemy应该在自己的内存中实现一种DB,使用自己的查询引擎,以及同步2个数据库所带来的所有问题?SQLAlchemy如何支持您查询的不同数据库的所有不同操作和功能?您的简单等式谓词示例只触及了表面

回滚时,回滚DB的事务(以及会话的未刷新更改)

  • 第二个原因是我采用了一个相当大的程序,它能够定义自己的会话,其实例也会导致flush提交

由事件处理错误引起。

您已禁用,因此您的
mario,none,False
工作正常。将对数据库触发查询,如果您没有手动或自动将会话中保留的更改刷新到数据库,则查询无法看到这些更改。而flush并不意味着提交,所以有其他事情发生了,您应该提供一个关于这一点的示例,或者更清楚地说明“回滚无效”的含义。我禁用了autoflush,因为正如您所提到的,我试图生成一个最小的示例,从,单元测试依赖于禁用自动刷新。»刷新并不意味着提交,因此出现了其他问题«,我试图在ghini.desktop代码中查找它,但我不认识它。而且这个代码远远不是最小的。»对DB启动一个查询«-因为
query
是会话的一个方法,我假设我是在查询会话,所以我很惊讶
session.query
session中不返回对象
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)