首页 > 解决方案 > 使用 session.query(cls).from_statement 多次插入“on conflict ...返回*”在提交之前不会反映更改

问题描述

我正在使用带有 postgres 11 的 sqlalchemy 1.3.0。我正在尝试使用INSERTwithON CONFLICT DO UPDATE ... RETURNING *来创建我的模型的实例。

class Model(Base):
    __tablename__ = 'mytable'
    pk = Column(String(64), primary_key=True)
    col2 = Column(String(64))

    @classmethod
    def upsert(cls, pk, col2, session):
        return session.query(cls).from_statement(
            text(
                """
                INSERT INTO {} (pk, col2) VALUES (:pk, :col2)
                ON CONFLICT (pk) DO UPDATE SET col2=EXCLUDED.col2 RETURNING *;
                """.format(cls.__tablename__)
            )
        ).params(pk=pk, col2=col2).one()

obj1 = Model.upsert(1, 'one', session)
obj2 = Model.upsert(1, 'two', session)
print(obj2.col2) ----> outputs "one"
session.commit()
print(obj2.col2) ----> outputs "two"

第二个 upsert 确实向数据库发出了正确的命令,但是打印返回对象的 col2 属性会显示在第一个 upsert 期间插入的列的。然后,如果我执行 a session.commit(),对象会神奇地更新以显示新值。我错过了什么?我希望从函数返回的对象反映行更新到的值,而不必进行提交,因为我希望这与其他几件事一起在事务中发生。

标签: postgresqlsqlalchemy

解决方案


事实证明,session.commit()本质上是使对象过期,因此以下内容print(obj2.col2)将重新查询数据库以填充其属性。

为了使 obj2.col2 成为正确的值而不提交(并重新查询数据库),我只需要session.expunge(obj1)在第二次 upsert 调用之前执行创建 obj2。


推荐阅读