You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLAlchemy before_update事件监听失效:无法记录更新前数据

问题解决:SQLAlchemy before_update无法记录更新前数据

问题根源

  1. before_update事件触发时,target对象的属性已经被赋值为更新后的值,直接读取无法获取旧数据。
  2. 日志表log_db仅存储了关联的appt_id,前端渲染时通过关联关系item.appt读取数据,此时appts_db表已经完成更新,所以新旧日志显示的都是最新数据。

解决方案

1. 修改日志表结构,增加存储新旧值的字段

更新app.py中的log_db模型,添加字段保存变更前后的具体数据:

from sqlalchemy import inspect
from datetime import datetime

class appts_db(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    carrier = db.Column(db.String(100))
    pickup_date = db.Column(db.String(10))
    log_id = db.relationship("log_db", backref="appt", lazy=True)

class log_db(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    appt_id = db.Column(db.Integer, db.ForeignKey('appts_db.id'))
    modified_on = db.Column(db.DateTime, default=datetime.now)
    action = db.Column(db.String(20))  # 扩展长度以容纳完整action描述
    # 存储旧值的字段
    old_carrier = db.Column(db.String(100))
    old_pickup_date = db.Column(db.String(10))
    # 存储新值的字段
    new_carrier = db.Column(db.String(100))
    new_pickup_date = db.Column(db.String(10))

2. 更新事件监听器,分别记录新旧值

使用SQLAlchemy的inspect工具获取对象的历史变更值,在before_update中记录旧值,after_update中记录新值:

@event.listens_for(appts_db, "before_update")
def update_log_old(mapper, connection, target):
    insp = inspect(target)
    old_values = {}
    # 遍历所有属性,提取有变更的旧值
    for attr in insp.attrs:
        if attr.history.has_changes():
            old_values[attr.key] = attr.history.deleted[0] if attr.history.deleted else None
    
    po = log_db.__table__
    connection.execute(po.insert().values(
        appt_id=target.id,
        action='Updated - Old',
        old_carrier=old_values.get('carrier'),
        old_pickup_date=old_values.get('pickup_date')
    ))

@event.listens_for(appts_db, "after_update")
def update_log_new(mapper, connection, target):
    po = log_db.__table__
    connection.execute(po.insert().values(
        appt_id=target.id,
        action='Updated - New',
        new_carrier=target.carrier,
        new_pickup_date=target.pickup_date
    ))

3. 修改前端模板,根据日志类型显示对应值

更新log.html,根据action字段判断显示旧值还是新值:

<table>
    <tr>
        <th>Action</th>
        <th>Carrier</th>
        <th>Pickup Date [YYYY-MM-DD]</th>
        <th>Date Modified</th>
    </tr>
    {% for item in log %}
    <tr>
        <td>{{ item.action }}</td>
        <td>
            {% if item.action == 'Updated - Old' %}
                {{ item.old_carrier }}
            {% else %}
                {{ item.new_carrier }}
            {% endif %}
        </td>
        <td>
            {% if item.action == 'Updated - Old' %}
                {{ item.old_pickup_date }}
            {% else %}
                {{ item.new_pickup_date }}
            {% endif %}
        </td>
        <td>{{ item.modified_on.strftime("%Y-%m-%d %H:%M:%S") }}</td>
    </tr>
    {% endfor %}
</table>

关键说明

  • inspect(target)可以获取对象的状态变更历史,attr.history.deleted存储了属性变更前的值。
  • 直接在日志表中存储变更数据,避免依赖关联查询导致的新旧数据混淆。

内容的提问来源于stack exchange,提问作者Brandon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 08:10:40