SQLAlchemy,scoped_session - 原始 SQL INSERT 不写入数据库

Paw*_*elP 5 python mysql sqlalchemy pyramid

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

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

    如果 session.twophase: AttributeError: 'scoped_session' object has没有属性'双相'"

什么有效但不可接受,因为它不使用 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)
Run Code Online (Sandbox Code Playgroud)

这是我为隔离问题而编写的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__()
Run Code Online (Sandbox Code Playgroud)

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

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

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

我无法理解为什么在原始 SQL 旁边使用 ORM 插入来“修复”它。

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

Spe*_*bun 5

自从我将原始 sql 与 sqlalchemy 混合以来已经有一段时间了,但是每当您混合它们时,您都需要了解 ORM 幕后发生的情况。首先,检查自动提交标志。如果 zope 事务配置不正确,ORM 插入可能会触发提交。

实际上,在查看了 zope 文档之后,手动执行语句似乎需要一个额外的步骤。从他们的自述中

默认情况下,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()
Run Code Online (Sandbox Code Playgroud)

看来你没有这样做,所以 transaction.commit 什么也不做。