使用 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_saveafter_update 回调中返回当前值。

attribute_before_last_save is invoked two ways 立即获取 after_saveafter_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 查询?

Yii ActiveRecord - 有没有办法只更新脏属性?

ActiveRecord 自定义 SQL 结果自动映射