【问题标题】:SQLAlchemy, scoped_session - raw SQL INSERT doesn't write to DBSQLAlchemy,scoped_session - 原始 SQL INSERT 不写入数据库
【发布时间】:2014-10-18 12:25:08
【问题描述】:

我有一个 Pyramid / SQLAlchemy,MySQL python 应用程序。 当我执行原始 SQL INSERT 查询时,没有任何内容写入数据库。 但是,当使用 ORM 时,我可以写入数据库。我阅读了文档,阅读了有关 ZopeTransactionExtension 的信息,阅读了大量 SO 问题,但都无济于事。 到目前为止没有奏效的地方:

  • transaction.commit() - 没有任何内容写入数据库。我确实意识到这个声明对于 ZopeTransactionExtension 是必要的,但它在这里并没有发挥作用。
  • dbsession().commit - 因为我使用的是 ZopeTransactionExtension,所以不起作用
  • dbsession().close() - 什么都没写
  • dbsession().flush() - 什么都没写
  • mark_changed(session) -

    文件“/home/dev/.virtualenvs/sc/local/lib/python2.7/site-packages/zope/sqlalchemy/datamanager.py”,第 198 行,在 join_transaction 如果 session.twophase: AttributeError: 'scoped_session' 对象没有属性 'twophase'"

什么有效但不可接受,因为它不使用 scoped_session:
  • engine.execute(...)

我正在寻找如何使用 scoped_session(我的代码中为 dbsession())执行原始 SQL

这是我的 SQLAlchemy 设置 (models/__init__.py)

def dbsession():
    assert (_dbsession is not None)
    return _dbsession

def init_engines(settings, _testing_workarounds=False):
    import zope.sqlalchemy
    extension = zope.sqlalchemy.ZopeTransactionExtension()
    global _dbsession

    _dbsession = scoped_session(
        sessionmaker(
            autoflush=True,
            expire_on_commit=False,
            extension=extension,
        )
    )

    engine = engine_from_config(settings, 'sqlalchemy.')
    _dbsession.configure(bind=engine)

这是我为隔离问题而编写的 python 脚本。它类似于问题发生的真实环境。我只想让下面的脚本将数据插入数据库:

# -*- coding: utf-8 -*-
import sys
import transaction

from pyramid.paster import setup_logging, get_appsettings
from sc.models import init_engines, dbsession
from sqlalchemy.sql.expression import text


def __main__():

if len(sys.argv) < 2:
    raise RuntimeError()
config_uri = sys.argv[1]
setup_logging(config_uri)
aa = init_engines(get_appsettings(config_uri))

session = dbsession()
session.execute(text("""INSERT INTO
         operations (description, generated_description)
         VALUES ('hello2', 'world');"""))

print list(session.execute("""SELECT * from operations""").fetchall()) # prints inserted data
transaction.commit()

print list(session.execute("""SELECT * from operations""").fetchall()) # doesn't print inserted data

if __name__ == '__main__':
    __main__()

有趣的是,如果我这样做:

session = dbsession()
session.execute(text("""INSERT INTO
         operations (description, generated_description)
         VALUES ('hello2', 'world');"""))
op = Operation(generated_description='aa', description='oo')
session.add(op)

然后第一个打印输出原始的SQL插入行('hello2''world'),第二个打印输出两行行,实际上两行都插入到数据库中。

我无法理解为什么将 ORM 插入与原始 SQL 一起“修复”它。

我确实需要能够在 scoped_session 上调用 execute() 以使用原始 SQL 将数据插入数据库。有什么建议吗?

【问题讨论】:

    标签: python mysql sqlalchemy pyramid


    【解决方案1】:

    我已经有一段时间没有将原始 sql 与 sqlalchemy 混合在一起了,但无论何时混合它们,您都需要了解 ORM 幕后发生的情况。首先,检查autocommit 标志。如果未正确配置 zope 事务,则 ORM 插入可能会触发提交。

    实际上,在查看 zope 文档后,手动执行语句似乎需要额外的步骤。来自他们的readme:

    默认情况下,zope.sqlalchemy 将会话置于“活动”状态 首次使用。 ORM 写操作自动将会话移动到一个 “改变”状态。这避免了不必要的数据库提交。有时它 需要通过SQL直接与数据库交互。它不是 可以猜测这样的操作是读还是写。因此我们 手动 SQL 语句写入时必须手动将会话标记为已更改 到数据库。

    >>> session = Session()
    >>> conn = session.connection()
    >>> users = Base.metadata.tables['test_users']
    >>> conn.execute(users.update(users.c.name=='bob'), name='ben')
    <sqlalchemy.engine...ResultProxy object at ...>
    >>> from zope.sqlalchemy import mark_changed
    >>> mark_changed(session)
    >>> transaction.commit()
    >>> session = Session()
    >>> str(session.query(User).all()[0].name)
    'ben'
    >>> transaction.abort()
    

    您似乎没有这样做,因此 transaction.commit 什么都不做。

    【讨论】:

    • 我尝试了你的建议,除了在 conn.execute() 中我通过了原始 sql。
    • 您的代码和我的要求有两个不同之处: 1. 我正在使用 scoped_session 2. 我正在传递原始 SQL 查询问题是,mark_changed(session) 抛出异常“AttributeError: 'scoped_session' 对象没有属性 'twophase'”。最后,我将有问题的代码重构为使用 ORM,在需要执行原始 SQL 的地方,我使用了 engine.execute()。我看不到使用 ZopeTransactionExtension 从 scoped_session 提交原始 SQL 的方法(除非我使用 autocommit=True)。
    • @PawelP 在 zope 文档中,他们传递给 scope_session 的选项之一是 twophase=TEST_TWOPHASE。也许这会解决它。
    • 我很确定这会有所帮助。我现在无法测试它,但我想你提供了所有可以提供的相关信息,所以我会接受你的回答。
    • @PawelP 您收到 AttributeError 是因为 mark_changed 需要实际的会话对象,而不是作用域会话(通常代理会话)。传递 twophaseoption 不会解决这个问题,但获取会话对象并使用它而不是作用域会话将。请参阅stackoverflow.com/a/21162302/3123187 了解如何执行此操作。
    猜你喜欢
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2018-11-29
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-18
    相关资源
    最近更新 更多