88

使用一个简单的示例进行设置:我有 1 个表 ( ),其中包含第二个表 ( )中每个记录的列Totals的总和。amountThings

当 athing.amount得到更新时,我想简单地将旧值和新值之间的差异添加到total.sum.

现在我正在减去self.amountduringbefore_update并添加self.amountduring after_update。这使更新成功的方式过于信任。

约束: 我不想简单地重新计算所有交易的总和。

问题:很简单,我想在after_update回调期间访问原始值。你想出了什么方法来做到这一点?

更新:我同意 Luke Francl 的想法。在after_update回调期间,您仍然可以访问self.attr_was正是我想要的值。我还决定使用一个after_update实现,因为我想在模型中保留这种逻辑。这样,无论我决定将来如何更新交易,我都会知道我正在正确更新交易的总和。感谢大家的实施建议。

4

10 回答 10

155

每个人都在谈论交易。

那就是说...

Rails 2.1 的 ActiveRecord 跟踪对象的属性值。所以如果你有一个属性total,你就会有一个total_changed?方法和一个total_was返回旧值的方法。

不再需要向模型添加任何内容来跟踪这一点。

更新:这是ActiveModel::Dirty要求的文档。

于 2009-03-03T16:33:45.163 回答
11

将“_was”附加到您的属性将在保存数据之前为您提供先前的值。

这些方法称为脏方法方法。

干杯!

于 2014-07-02T10:09:53.470 回答
10

其他一些人提到将所有这些都包含在事务中,但我认为这已经为您完成了;您只需要通过在 after_* 回调中引发错误来触发回滚。

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

保存、保存!或销毁调用的整个回调链在事务中运行。这包括 after_* 钩子。如果一切顺利,则在链完成后执行 COMMIT。

如果 before_* 回调取消了操作,则会发出 ROLLBACK。您还可以触发 ROLLBACK 在任何回调中引发异常,包括 after_* 挂钩。但是请注意,在这种情况下,客户端需要注意它,因为普通保存会引发此类异常,而不是悄悄返回 false。

于 2009-03-03T16:47:01.683 回答
10

要获取所有更改的字段,分别具有它们的旧值和新值:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}
于 2013-08-05T18:55:01.523 回答
6

ActiveRecord::Dirty是一个内置在 ActiveRecord 中的模块,用于跟踪属性更改。所以你可以thing.amount_was用来获取旧值。

于 2009-03-03T17:08:05.733 回答
3

将此添加到您的模型中:

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

然后在 after_update 代码中使用 @old_amount。

于 2009-03-03T16:32:17.053 回答
3

如果要在更新后获取特定字段的值,可以使用 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)
于 2020-11-10T06:53:02.367 回答
1

Rails 5.1开始,inside of after 回调的行为发生attribute_was了变化。attribute_was将在保存完成后返回值,并在after_saveorafter_update回调中返回当前值。

attribute_before_last_save被调用两种方式after_save来获取和after_update回调中字段的前一个值:

选项1

attribute_before_last_save('yourfield')

选项#2

*_before_last_save
于 2021-01-23T00:19:04.410 回答
0

首先,您应该在事务中执行此操作,以确保您的数据一起写入。

要回答您的问题,您可以在 before_update 中将一个成员变量设置为旧值,然后您可以在 after_update 中访问它,但这不是一个非常优雅的解决方案。

于 2009-03-03T16:28:14.380 回答
0

想法 1:将更新包装在数据库事务中,这样如果更新失败,您的 Totals 表就不会更改:ActiveRecord Transactions docs

想法 2:在 before_update 期间将旧值存储在 @old_total 中。

于 2009-03-03T16:28:36.283 回答