【问题标题】:Skip the default on onupdate defined, for specific update queries in SQLAlchemy跳过定义 onupdate 的默认值,用于 SQLAlchemy 中的特定更新查询
【发布时间】:2021-12-06 01:38:33
【问题描述】:

如果我有一个帖子列表,其中包含 createdupdated 日期以及默认附加的 onupdate 回调。

有时我需要为inappropriate reports 或类似操作标记帖子。我不希望修改 createdupdated 日期。

如何在执行update 操作时跳过定义的onupdate

【问题讨论】:

  • 在回调中使用if 来检查标志并仅在未更改时才更新有任何限制吗?
  • 如何将参数传递给这个onupdate 函数?我正在朝那个方向寻找,如果找到应该可以工作。
  • 您的模型中可能有不持久的属性,这些属性可以作为参数值。 this 可能会有所帮助
  • 这不起作用,因为在调用 onupdate 函数之前删除了非持久属性,否则 db.session.commit() 似乎会抛出错误。我可能做错了什么。试图弄清楚。
  • 我没有解决方案,我认为标志将被保留并且可以在更新时检查。将这些要点添加到您尝试过的问题中会有所帮助。

标签: python python-3.x flask sqlalchemy flask-sqlalchemy


【解决方案1】:

SQLAlchemy 将应用默认的when

没有为该列的 INSERT 或 UPDATE 语句提供值

但是明显的解决方法 - 将列显式设置为其当前值 - 将不起作用,因为会话 checks 是否该值实际已更改,并且如果没有则不传递值。这里有两种可能的解决方案,假设 SQLAlchemy 1.4+ 和这个模型:

class Post(db.Model):
    flag = db.Column(db.Boolean, default=False)
    last_updated = db.Column(db.DateTime, default=some_func, onupdate=some_func)

使用事件监听器

添加一个before update listener 来检测标志列何时被修改,mark 时间戳列被修改,即使它的值没有改变。这将使 SQLAlchemy 将当前值添加到更新中,因此不会调用 onupdate 函数。

import sqlalchemy as sa
...
@sa.event.listens_for(Post, 'before_update')
def receive_before_update(mapper, connection, target):
    insp = sa.inspect(target)
    flag_changed, _, _ = insp.attrs.flag.history
    if flag_changed:
        orm.attributes.flag_modified(target, 'last_updated')

使用 SQLAlchemy 核心而不是 ORM

SQLAlchemy 核心不需要会话,因此可以将当前时间戳值传递给更新以避免触发 onupdate 函数。 ORM 将不知道以这种方式进行的任何更改,因此如果在会话的上下文中完成,受影响的对象应该被刷新或过期。这是一个“快速而肮脏”的解决方案,但如果标记发生在正常应用程序流程之外,可能就足够了。

with db.engine.begin() as conn:
    posts = Post.__table__
    update = sa.update(posts).where(...).values(flag=True, last_updated=posts.c.last_updated)
    conn.execute(update)

【讨论】:

  • 我更改了事件监听器类型 - 映射器事件作用于实例,而原始 before_flush 会话事件需要遍历 session.dirty 才能找到修改后的 Post 实例。
猜你喜欢
  • 1970-01-01
  • 2014-04-03
  • 2014-04-02
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
  • 2013-06-24
  • 2010-11-08
  • 2018-01-23
相关资源
最近更新 更多