SQLAlchemy before_update事件监听失效:无法记录更新前数据
问题解决:SQLAlchemy before_update无法记录更新前数据
问题根源
before_update事件触发时,target对象的属性已经被赋值为更新后的值,直接读取无法获取旧数据。- 日志表
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
相关产品推荐
相关产品推荐

