sqlalchemy.exc.ResourceClosedError: 在select之后插入时,此连接已关闭

9

我正在从SQLite数据库中执行select(),然后再执行insert()

engine = create_engine('sqlite:///testdb.db')
metadata = MetaData(bind=engine)
test = Table('test', metadata, autoload=True)

# Select all from pending_data
sel = select([test])
res = engine.execute(sel)

print res

# do an insert into pending_data
test.insert()\
    .values(info='blah')\
    .execute()

当我的代码执行插入行时,我会收到以下错误提示:
sqlalchemy.exc.ResourceClosedError: This Connection is closed

不过,如果我将我的res转换为列表,像这样:

res = list(engine.execute(sel))

我的代码运行良好。这里发生了什么事情?

3个回答

28

SQLAlchemy有两个概念需要注意:连接和引擎。一个引擎可以支持多个同时连接。在您的示例中,您将表绑定到一个引擎上。现在,每当您调用.execute时,都会为执行的每个单个查询创建一个新连接。但是sqlite3只允许1个同时“连接”。

修复此问题的最佳方法是显式地创建连接并使用它,而不是使用引擎自动创建的连接;并使用with语句使用连接,这可以确保在块结束时关闭连接:

engine = create_engine('sqlite:///testdb.db')
metadata = MetaData(bind=engine)
test = Table('test', metadata, autoload=True)

with engine.connect() as connection:
    # Select all from pending_data
    sel = select([test])
    res = connection.execute(sel)

    # do an insert into pending_data
    connection.execute(test.insert().values(info='blah'))
为了理解这种行为,错误发生的原因是您在隐式创建和持有的集合中保留了一个活动游标(由变量res引用; 在消耗、关闭或丢弃对其的引用之前,游标以及连接将一直保持活动状态,并且数据库将被锁定)。
当您执行list(res)时,您正在使用游标并它会被SQLAlchemy关闭;如果结果的引用计数降至0,也会发生同样的情况。
您还可以尝试以下操作来了解这一点,它们会按照您的预期工作:
res = engine.execute(sel)
print(res)
res.close()  # close explicitly
或者
res = engine.execute(sel)
print(res)
del res  # drop the only reference to res

因此,在使用完ResultProxy之后,请完全消费它或显式地关闭它,或者在完成后删除对它的引用。

如果您重用同一连接,这不是问题;只有当您创建到sqlite3数据库(postgresql、mysql、oracle等也可以)的新连接时才需要考虑此问题。


“drop references to it” 是什么意思? - mchangun
当没有名称或其他对象指向它们时,对象将被释放。 - Antti Haapala -- Слава Україні

0
对我来说,当我尝试为一个表发布条目时,我遇到了这个错误。我正在使用sqlachmey的新语法,但我忘记了在使用select时添加表类名,所以我写成了select().where...而不是select(tbaleNameClass).where...,因此我得到了这个ResourceClosedError
sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.

所以我需要做的唯一一件事就是正确地编写语法,而不是写成

  query = select().where(
        Assessment.created_by == assessment.created_by)

写入
  query = select(Assessment).where(
        Assessment.created_by == assessment.created_by)

评估(Assessment)是表格类名。所以,如果你们中的任何人遇到了同样的错误,请首先检查你是否正确地书写了代码 :)。

0
对我来说,我遇到了类似的问题。我检查了连接对象,发现该对象有一个属性:“closed”,在之前的选择查询后变为True。所以我重新创建了连接。这样就解决了问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接