flask SQLAlchemy 报错 Detached InstanceError...

Detached InstanceError:Instance is not bound to a Session 关闭session后使用SQLAlchemy对象_Goodwillie的博客-CSDN博客

报错详情

1
2
sqlalchemy.orm.exc.DetachedInstanceError: Instance <EntityXxxxxx at 0x32768d0> is not bound to a Session; attribute refresh operation cannot proceed (Ba
ckground on this error at: http://sqlalche.me/e/bhk3)

分析

这种情况主要是发生在关闭数据库会话后,使用 ORM 获取对象的某些字段值时报错。

报错的原因也很简单,SQLAlchemy 的 ORM 方式将数据库中的记录映射成了我们定义好的模型类,这些类对象的实例只在数据库会话(session)的生命期内有效,假如我们将数据库会话关闭了,再访问数据表类的对象就会报错。类似代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class User(Base):
__tablename__ = 'USER'

id = Column(Integer, Sequence('USER_SEQ'), primary_key=True, autoincrement=True)
name = Column(String(30))
age = Column(Integer)
status = Column(String(30))


engine = create_engine(str(db_url), encoding=b'utf-8', echo=echo, convert_unicode=True)
Session = sessionmaker(bind=engine)
session = Session()

user = User(name='John', age=30)
session.add(user)
session.commit()
session.close()

print(user.name)

运行到最后一行会抛出上述异常。

解决方案

  1. 不关闭 session,此时获取该对象不报错。

  2. 根据分析,关闭 session 的同时,session 的数据也会同步清除,那么我们可以在关闭 session 的时候,把数据保留下来, 使用 session.expunge(user) 可以把该对象分离并保存下来

    • Session.expunge() 是一种将对象从session中移除的方法。cascade取值为expunge时的意义为:当父对象进行了expunge操作时,该对象当时关联的子对象也会被从session中删除。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    user = User(name='John', age=30)
    session.add(user)
    session.commit()

    session.refresh(user)
    session.expunge(user)

    session.close()
    print(user.name)
  3. 自己将 orm 对象转换为一个属性相同的普通对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class User(Base):
    __tablename__ = 'USER'

    id = Column(Integer, Sequence('USER_SEQ'), primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    status = Column(String(30))

    def __repr__(self):
    return "<%s:%s:%s>" % (self.name, self.age, self.status)

    def freeze(self):
    """
    :return:
    """
    db.session.refresh(self)
    user = UserFreeze()
    user.name = self.name
    user.age = self.age
    user.status = self.status
    return user

flask SQLAlchemy 报错 Detached InstanceError...
https://flepeng.github.io/021-Python-32-框架-Flask-flask-SQLAlchemy-报错-Detached-InstanceError/
作者
Lepeng
发布于
2021年3月31日
许可协议