使用sqlalchemy的postgresql JSON类型列表

时间:2014-08-14 05:05:23

标签: python postgresql sqlalchemy pyramid

我正在使用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))

数据库不会更新,它仍然是[' 45']并打印返回

True, False

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

3 个答案:

答案 0 :(得分: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。但是,上面关于变异跟踪的内容也适用于{{1}}类型的列。

答案 1 :(得分: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 :(得分:-2)

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

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