对于SQLAlchemy中的特定更新查询,跳过默认的onupdate定义



如果我有一个帖子列表,其中createdupdated日期默认附加onupdate回调

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

我怎么能跳过定义的onupdate,而使update的行动?

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

然而,明显的解决方法——显式地将列设置为当前值——将不起作用,因为会话检查值是否实际发生了变化,如果没有,则不传递值。下面是两种可能的解决方案,假设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侦听器,用于检测标志列何时被修改,并将时间戳列标记为已修改,即使其值未改变。这将使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)

您可以简单地使用SQLAlchemy Core level Column对象分配列,以发出像'UPDATE post set created=post '这样的SQL。创建、更新= post.updated ' .

class Post(Base):
id=Column(Integer,primary_key=True)
name=Column(String)
created=Column(DateTime,onupdate=func.now())
updated=Column(DateTime,onupdate=func.now())
post=session.query(Post).first()
post.name='updated post'
post.created= Post.__table__.c.created
post.updated= Post.__table__.c.updated
session.commit()

SQL emmitted:

UPDATE post SET name='updated post', created=post.created, updated=post.updated WHERE post.id=1;

最新更新