使用 ActiveRecord,有没有办法在 after_update 期间获取记录的旧值
Posted
技术标签:
【中文标题】使用 ActiveRecord,有没有办法在 after_update 期间获取记录的旧值【英文标题】:Using ActiveRecord, is there a way to get the old values of a record during after_update 【发布时间】:2010-10-11 01:27:40 【问题描述】:使用简单示例进行设置:我有 1 个表 (Totals
),其中包含第二个表 (Things
) 中每条记录的 amount
列的总和。
当thing.amount
更新时,我想简单地将旧值和新值之间的差异添加到total.sum
。
现在我在before_update
期间减去self.amount
并在after_update
期间添加self.amount
。这使更新成功的方式过于信任。
约束:我不想简单地重新计算所有交易的总和。
问题:很简单,我想在after_update
回调期间访问原始值。你想出了什么方法来做到这一点?
更新:我同意 Luke Francl 的想法。在after_update
回调期间,您仍然可以访问self.attr_was
值,这正是我想要的。我还决定使用after_update
实现,因为我想在模型中保留这种逻辑。这样,无论我决定将来如何更新交易,我都会知道我正在正确更新交易的总和。感谢大家的实施建议。
【问题讨论】:
【参考方案1】:每个人都在谈论交易。
也就是说……
从 Rails 2.1 开始的 ActiveRecord 跟踪对象的属性值。所以如果你有一个属性total
,你就会有一个total_changed?
方法和一个返回旧值的total_was
方法。
不再需要向模型添加任何内容来跟踪这一点。
更新:这是ActiveModel::Dirty 的文档。
【讨论】:
我认为存在这样的东西。您可以链接到有关 attr_changed 的相关文档吗? attr_was? 当然,我添加了指向答案的链接。【参考方案2】:将“_was”附加到您的属性将在保存数据之前为您提供先前的值。
这些方法称为dirty methods 方法。
干杯!
【讨论】:
【参考方案3】:其他一些人提到将所有这些都包含在事务中,但我认为这对你来说已经完成了; 您只需通过在 after_* 回调中引发错误异常来触发回滚。
见http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
保存、保存!或销毁调用的整个回调链在事务中运行。这包括 after_* 钩子。如果一切顺利,则在链完成后执行 COMMIT。
如果 before_* 回调取消操作,则会发出 ROLLBACK。您还可以触发 ROLLBACK 在任何回调中引发异常,包括 after_* 挂钩。但是请注意,在这种情况下,客户端需要注意它,因为普通保存会引发此类异常,而不是悄悄返回 false。
【讨论】:
【参考方案4】:要获取所有更改的字段,分别包含它们的旧值和新值:
person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes # => "name" => ["Bill", "Bob"]
【讨论】:
我相信最后一行现在应该是person.previous_changes
而不是 person.changes
【参考方案5】:
ActiveRecord::Dirty 是一个内置在 ActiveRecord 中的模块,用于跟踪属性更改。所以你可以使用thing.amount_was
来获取旧值。
【讨论】:
【参考方案6】:将此添加到您的模型中:
def amount=(new_value)
@old_amount = read_attribute(:amount)
write_attribute(:amount,new_value)
end
然后在 after_update 代码中使用 @old_amount。
【讨论】:
【参考方案7】:如果您想在更新后获取特定字段的值,可以使用 field_before_last_save 方法。
Example:
u = User.last
u.update(name: "abc")
u.name_before_last_save will give you old value (before update value)
【讨论】:
【参考方案8】:从 Rails 5.1 开始,attribute_was
在 after 回调中的行为发生了变化。 attribute_was
将在保存完成后返回值,并在 after_save
或 after_update
回调中返回当前值。
attribute_before_last_save
is invoked two ways 立即获取 after_save
和 after_update
回调中字段的先前值:
选项#1
attribute_before_last_save('yourfield')
选项#2
*_before_last_save
【讨论】:
【参考方案9】:首先,您应该在事务中执行此操作,以确保您的数据一起写入。
要回答您的问题,您可以在 before_update 中将成员变量设置为旧值,然后您可以在 after_update 中访问它,但这不是一个非常优雅的解决方案。
【讨论】:
我正在努力了解如何使用事务来实现我想要的。像这样的事务是存在于模型中还是存在于控制器中?我要删除我的“after_update”和“before_update”回调吗?最后,如何获得我需要的旧值以确定差异? 没关系,我看到我必须将代码放在控制器中。我们很好。【参考方案10】:想法 1:将更新包装在数据库事务中,这样如果更新失败,您的 Totals 表不会更改:ActiveRecord Transactions docs
想法 2:在 before_update 期间将旧值存储在 @old_total 中。
【讨论】:
以上是关于使用 ActiveRecord,有没有办法在 after_update 期间获取记录的旧值的主要内容,如果未能解决你的问题,请参考以下文章
使用 ActiveRecord,有没有办法在 after_update 期间获取记录的旧值
有没有办法选择一个 ActiveRecord 关系进行更新?
有没有办法在后续 Rails 3 ActiveRecord 迁移中删除 id 列?
有没有办法反转 ActiveRecord::Relation 查询?