FLASK SQLAlchemy sqlalchemy.exc.ResourceClosedError: This transaction is closed 报错

问题遇到的现象和发生背景

在学习SQLAlchemy时

遇到的现象和发生背景,请写出第一个错误信息

出现错误 Resource.Closed.Error : this transaction is closed

用代码块功能插入代码,请勿粘贴截图。 不用代码块回答率下降 50%
@db.event.listens_for(User, 'after_delete', named=True)
def delete_account(**kwargs) :
    target =kwargs['target']
    for username in [target.username] :
        if username is not None :
            dis = Photo.query.filter_by(user_name=username).update({'user_name':'Disposed'})
            db.session.commit()
class User(db.Model) :
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), unique=True)
    password = db.Column(db.String(30))
    photos = db.relationship('Photo', back_populates='users', cascade='all')


class Photo(db.Model) :
    id = db.Column(db.Integer, primary_key=True)
    photoname =db.Column(db.String(127), unique=True)
    purename = db.Column(db.String(127), unique=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    users = db.relationship('User', back_populates='photos')
    user_name = db.Column(db.String(30), unique=True)
运行结果及详细报错内容

Traceback (most recent call last):
File "/usr/lib/python3.10/code.py", line 90, in runcode
exec(code, self.locals)
File "", line 1, in
File "", line 2, in commit
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 1451, in commit
self._transaction.commit(_to_root=self.future)
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 829, in commit
self._prepare_impl()
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 808, in _prepare_impl
self.session.flush()
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 3444, in flush
self.flush(objects)
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 3583, in flush
with util.safe_reraise():
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/util/langhelpers.py", line 84, in exit
compat.raise
(value, with_traceback=traceback)
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/util/compat.py", line 210, in raise

raise exception
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 3584, in _flush
transaction.rollback(_capture_exception=True)
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 851, in rollback
self._assert_active(prepared_ok=True, rollback_ok=True)
File "/usr/local/lib/python3.10/dist-packages/sqlalchemy/orm/session.py", line 617, in _assert_active
raise sa_exc.ResourceClosedError(closed_msg)
sqlalchemy.exc.ResourceClosedError: This transaction is closed

我的解答思路和尝试过的方法,不写自己思路的,回答率下降 60%

尝试修改Photo表

我想要达到的结果,如果你需要快速回答,请尝试 “付费悬赏”

这个错误信息表明你在调用 db.session.commit() 时,会话已经被关闭了。这通常是因为在你之前调用过 db.session.rollback() 或者 db.session.close()。

在你的代码中,你在 delete_account 函数里调用了 db.session.commit(),但是你并没有在这个函数内部打开一个新的事务。因此,当你尝试在会话已经关闭的情况下调用 commit() 时,会抛出上面这个异常。

一种可能的解决方法是,在 delete_account 函数内部打开一个新的事务。例如:


```python
@db.event.listens_for(User, 'after_delete', named=True)
def delete_account(**kwargs) :
    target =kwargs['target']
    for username in [target.username] :
        if username is not None :
            dis = Photo.query.filter_by(user_name=username).update({'user_name':'Disposed'})
            db.session.begin()
            db.session.commit()


需要注意的是, 您使用了SQLAlchemy的事件监听机制来监听 User 模型的after_delete事件,在删除User之后,相关的Photo的user_name会被更新。然而, 您的代码中并没有考虑到在处理这个事件的同时,会话已经被关闭了。

解决方法之一是,确保在你的函数内部打开一个新的事务。 更一般地,你需要确保在所有数据库操作之前会话是打开的,并在所有操作之后关闭会话。

另外需要注意的是,在修改photo表的user_name后, 你需要重新加载photo的信息,或者将其返回到会话中,否则你将在进行其他操作时遇到相似的错误。

```python
@db.event.listens_for(User, 'after_delete', named=True)
def delete_account(**kwargs) :
    target =kwargs['target']
    for username in [target.username] :
        if username is not None :
            dis = Photo.query.filter_by(user_name=username).update({'user_name':'Disposed'})
            db.session.flush()
            #re-load the photo data
            Photo.query.filter_by(user_name='Disposed').all()


这些修改将有助于消除错误, 但是需要确保其他相关部分不会出现类似的错误。
如果我的回答对您解决问题有帮助,请您及时采纳,非常感谢。

望采纳!!!!点击回答右侧即可采纳
这个报错可能是因为你在操作数据库的事务已经关闭了,而你在调用db.session.commit()时,由于事务已经关闭,导致这个错误。

你可以在你的代码中加入一个判断,来避免在事务已经关闭的情况下调用db.session.commit(),例如:

if db.session.is_active:
db.session.commit()

另外,你也可以尝试在你的代码中加入一个try-except语句,来处理这个异常,例如:

try:
db.session.commit()
except sqlalchemy.exc.ResourceClosedError as e:
print(e)


1、sqlalchemy.exc.OperationalError 的解决办法
经过判断 :该错误是由于数据库驱动版本与Python使用的驱动版本不兼容导致的,连接失效问题,典型特征使用连接执行一次操作后再执行第二次操作报次错误,解决方案换低版本的数据库连接试试。
于是决定进行数据库重装

2、sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1170, “BLOB/TEXT column ‘c_advice’ used in key specification without a key length”)的解决办法
原因 :是DataFrame对象索引的数据类型是TEXT/BLOB或其从属的类型,当将其作为mysql中的主键的时候,如果这些数据类型缺少明确的长度值,mysql无法保证主键的唯一性,因为这个主键是一个变量,其长度是动态的。
办法 由于数据的长度必须提供,(1)更换主键;(2)尝试使用VARCHAR并设置长度

原因分析:
执行db.session.delete(user)触发delete_account函数,delete_account最后的db.session.commit()提交后就关闭事务了,所以print(111111111)后边的db.session.commit()就会报错
解决办法:
注释掉上面的db.session.commit()即可
其它问题:
photos = db.relationship('Photo', back_populates='users', cascade='all')
这一行代码中的cascade='all'在删除User之后会级联删除Photo,是不是逻辑不太对,所以测试的时候就注释了

这是我的测试代码:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///flask.db"
db = SQLAlchemy(app=app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), unique=True)
    password = db.Column(db.String(30))
    # photos = db.relationship('Photo', back_populates='users', cascade='all')


class Photo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    photoname = db.Column(db.String(127), unique=True)
    purename = db.Column(db.String(127), unique=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    # users = db.relationship('User', back_populates='photos')
    user_name = db.Column(db.String(30), unique=True)


@db.event.listens_for(User, 'after_delete', named=True)
def delete_account(**kwargs):
    print(kwargs)
    target = kwargs['target']
    for username in [target.username]:
        if username is not None:
            print(username)
            dis = Photo.query.filter_by(user_name=username).update({'user_name': 'aaa'})
            # db.session.commit()


if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        user = db.session.query(User).filter(User.username == "test").first()
        db.session.delete(user)
        print(111111111)
        db.session.commit()

这个错误的原因是,在提交更新之前,数据库会话(session)已经被关闭了。可能在某个地方调用了 session.close() 或 session.remove(),导致当前的数据库会话关闭。

要解决这个问题,可以在调用 session.commit() 之前确保数据库会话是打开的。可以使用 try-except 语句来捕获异常,并在发生异常时回滚事务,如下所示:

try:
    db.session.commit()
except Exception as e:
    db.session.rollback()
    raise e

也可以使用上下文管理器(context manager)来管理事务,确保在操作结束时自动提交或回滚事务:

with db.session.begin():
    # 数据库操作
    # ……
    db.session.commit()  # 自动提交事务

如果上下文管理器块中发生了异常,事务将自动回滚。
仅供参考,望采纳,谢谢。

这个错误可能是因为你在尝试对已关闭的数据库连接或事务进行操作。这个错误通常是由于在数据库会话或事务已经关闭的情况下,你还在试图对其进行操作。

有几种方法可以解决这个问题:

1、确保你的数据库会话或事务在你尝试对其进行操作之前是打开的。

2、在尝试对数据库会话或事务进行操作之前,先检查它是否已经关闭。

3、在你的代码中捕获 sqlalchemy.exc.ResourceClosedError 异常,然后重新打开数据库会话或事务。

try:
    # perform database operation
except sqlalchemy.exc.ResourceClosedError:
    # reopen database session or transaction

希望能帮助到你!

当你在使用 SQLAlchemy 时遇到 ResourceClosedError 异常,通常是因为你试图对一个已关闭的资源进行操作。在这种情况下,资源可能是数据库连接、会话或事务。

解决 ResourceClosedError 异常的方法取决于具体原因。一些可能的解决方案如下:

如果你在使用数据库连接时遇到了 ResourceClosedError 异常,可能是因为连接已关闭或连接池已耗尽。你可以尝试重新建立数据库连接,或者增加连接池的大小。
如果你在使用会话时遇到了 ResourceClosedError 异常,可能是因为会话已关闭或被回收。你可以尝试重新创建会话,或者在使用完会话后显式地调用 session.close() 方法关闭会话。
如果你在使用事务时遇到了 ResourceClosedError 异常,可能是因为事务已关闭或已提交。你可以尝试在事务中重新调用 session.begin() 方法,或者在提交事务后重新创建事务。

这里有一些问题会导致你的代码抛出sqlalchemy.exc.ResourceClosedError 异常

1.你没有在调用update之前开始一个新的事务
2.你的update操作会commit掉你的session
3.你没有在调用 delete 之前关闭你的session

下面是改进后的代码:

@db.event.listens_for(User, 'after_delete', named=True)
def delete_account(**kwargs) :
    target =kwargs['target']
    for username in [target.username] :
        if username is not None :
            dis = Photo.query.filter_by(user_name=username).update({'user_name':'Disposed'})
            db.session.commit()
            db.session.remove() #  close session after commit

class User(db.Model) :
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), unique=True)
    password = db.Column(db.String(30))
    photos = db.relationship('Photo', back_populates='users', cascade='all')

class Photo(db.Model) :
    id = db.Column(db.Integer, primary_key=True)
    photoname =db.Column(db.String(127), unique=True)
    purename = db.Column(db.String(127), unique=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    users = db.relationship('User', back_populates='photos')
    user_name = db.Column(db.String(30), unique=True)


代码在删除用户后,在更新其他表之前先关闭了session,并且在更新操作之后commit,最后在关闭session.这样就可以避免了 ResourceClosedError 的问题.