【问题标题】:using list on postgresql JSON type with sqlalchemy使用带有 sqlalchemy 的 postgresql JSON 类型的列表
【发布时间】:2014-10-07 15:44:48
【问题描述】:

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


    【解决方案1】:

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

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

    当您为“复杂类型”的列(例如 dict 或列表)分配新值时,它也可以工作:

    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 类型的列。

    【讨论】:

    • @Sergey 您能否详细说明如何将Column(JSON) 更改为Column(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)
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 第二部分是在提交之后,抱歉没那么清楚
      • 嗯,好的,这是后续请求。我想控制台中一定有一个错误。 development.ini 中的 SQLAlchemy 警告设置有多详细?您还可以提前刷新以查看是否发出任何错误。
      猜你喜欢
      • 1970-01-01
      • 2014-07-15
      • 2023-02-09
      • 2018-12-23
      • 2015-09-28
      • 2018-03-24
      • 1970-01-01
      • 2020-05-15
      • 1970-01-01
      相关资源
      最近更新 更多