SQLAlchemy使用合并操作更新PostgreSQL数组无法正常工作

6
我正在使用SQLAlchemy来访问PostgreSQL数据库,并且我定义了以下这个对象:

我正在使用SQLAlchemy访问PostgreSQL数据库,对象定义如下:

class SessionLog(Base):
    __tablename__ = 'session_log'

    id = Column(Integer, primary_key=True)
    recordFile = Column('record_file', String(128))
    appSrcPorts = Column('app_src_ports', ARRAY(Integer))
    info5 = Column('info5', String(100))

我会选择并更新session_log表格,代码如下:

session = Session()
sessionLog = session.query(SessionLog).filter_by(id=sessionLogId).first()
sessionLog.appSrcPorts.append(1)
session.merge(sessionLog)
session.commit()

但是很奇怪的是,在我调用merge()和commit()之后,列'app_src_ports'没有更新。我找到了一个丑陋的方法来使它工作,在append()行之前添加这个:
sessionLog.appSrcPorts = list(sessionLog.appSrcPorts)

有人能告诉我为什么吗?


你的解决方案 (sessionLog.appSrcPorts = list(sessionLog.appSrcPorts) 将它转换为列表非常有效 :) - Sander van den Oord
2个回答

5

我找到了这个Gist,对我很有帮助。我添加了一个自定义的__setitem__,可以改变列表中的项,以及__delitem__删除一个项目:

from sqlalchemy.ext.mutable import Mutable
from sqlalchemy.dialects.postgresql import ARRAY

class MutableList(Mutable, list):

    def __setitem__(self, key, value):
        list.__setitem__(self, key, value)
        self.changed()

    def __delitem__(self, key):
        list.__delitem__(self, key)
        self.changed()

    def append(self, value):
        list.append(self, value)
        self.changed()

    def pop(self, index=0):
        value = list.pop(self, index)
        self.changed()
        return value

    @classmethod
    def coerce(cls, key, value):
        if not isinstance(value, MutableList):
            if isinstance(value, list):
                return MutableList(value)
            return Mutable.coerce(key, value)
        else:
            return value

然后在你的模型中:
your_field = db.Column(
    MutableList.as_mutable(ARRAY(db.String())),
    server_default="{}"
)

3
SQLA提供了MutableList作为扩展,不再需要自定义实现(大多数情况下,不提供嵌套对象的跟踪):https://docs.sqlalchemy.org/en/latest/orm/extensions/mutable.html#sqlalchemy.ext.mutable.MutableList - Ilja Everilä

5

SQLAlchemy ORM 依赖于事件检测来判断数据何时发生更改,从而需要刷新数据。在这种情况下,您正在就地更改 Python 数组值,这默认情况下不会产生任何事件。为使其工作,您需要同时使用 可变扩展 和 ARRAY 类型(以及发送这些事件的 list 子类)来将更改作为与父 SessionLog 对象相关的事件发送。


你认为这是SQLAlchemy的一个bug吗? - leeyiw
1
我们应该在sqlalchemy.ext.mutable中准备好一个“MutableList”实现,但我们现在没有(只有MutableDict)。ORM可以引入一种系统,使类型能够宣传其“可变”版本,从而减少使用可变类型的标准情况(如ARRAY)所需的样板文件数量。但这些都将是增强功能,我们不称之为错误。 - zzzeek

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