使用sqlalchemy的postgresql JSON类型列表

问题描述 投票:8回答:3

我使用sqlalchemy金字塔,pyramid_tm和postgresql来测试这个。

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()


class MyList(Base):
    id = Column(Integer, primary_key=True)
    lst = Column(JSON)

我正在使用postgresql 9.3+并使用JSON类型。当我这样做

mylst = MyList(lst=[])

我可以看到在数据库中也创建了空[]列表,并且

def view(request):
    mylst = DBSession.query(MyList).get(1)
    mylst.lst.append('45')
    print(DBSession.is_active, DBSession.is_modified(mylst))

我可以在数据库中看到['45'],并打印返回

True, True

在下一个请求上继续[编辑](上面已经提交)

def view(request):
    mylst = DBSession.query(MyList).get(1)
    mylst.lst.append('65')
    print(DBSession.is_active, DBSession.is_modified(mylst))

db不会更新,它仍然是['45']并且打印返回

True, False

我做错了什么或这是一个错误?

python postgresql sqlalchemy pyramid
3个回答
10
投票

默认情况下,SQLAlchemy仅跟踪值本身的更改,这对于简单值(如int和字符串)“按预期”工作:

alice.name = "Alice"
alice.age = 8

当您为“复杂类型”的列(例如dict或list)分配新值时,它也有效:

alice.toys = ['doll', 'teddy bear']

但是,如果修改列表中的某个元素,或者追加/删除值,SQLAlchemy不会注意到更改:

alice.toys[0] = 'teapot'
alice.toys.append('lego bricks')

要使这项工作,您可以确保每次都分配一个新列表:

toys = alice.toys[:]  # makes a "clone" of the existing list
toys[0] = 'teapot'
toys.append('lego bricks')
alice.toys = toys

或者阅读SQLAlchemy文档中的Mutation Tracking章节,了解如何对列表或字典进行子类化,以便跟踪元素的修改。

此外,既然你提到你正在使用Postgres - 在Postgres中有一个专用的ARRAY类型,你可以使用它来代替JSON,如果你需要的只是存储列表。然而,上面关于突变跟踪的内容也适用于ARRAY类型的列。


2
投票

您可以将实例标记为手动修改

from sqlalchemy.orm.attributes import flag_modified

def view(session):
    mylst = Session.query(MyList).get(1)
    mylst.lst.append('45')
    flag_modified(mylst, 'lst') # flag its `lst' attribute is modified
    print(Session.is_active, Session.is_modified(mylst))
    # (True, True)

-2
投票

DBSession.flush()之后尝试mylst.lst.append('45')。这允许您在pyramid_tm执行提交之前更新数据库。

更多信息可以在这里找到:http://docs.sqlalchemy.org/en/latest/orm/session.html#flushing

© www.soinside.com 2019 - 2024. All rights reserved.