使用ActiveRecord,有一种方法可以在after_update期间获取记录的旧值


81

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

thing.amount得到更新,我想简单地将旧值和新值之间的差异total.sum

现在,我要self.amount在期间减去before_updateself.amount在期间添加after_update。这使WAY对更新的成功过于信任。

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

问题:很简单,我想在after_update回调期间访问原始值。您想出了什么方法呢?

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

Answers:


148

大家都在谈论交易。

那说...

从Rails 2.1开始,ActiveRecord会跟踪对象的属性值。因此,如果您具有attribute total,则将有一个total_changed?方法和一个total_was返回旧值的方法。

无需在模型中添加任何内容即可继续跟踪。

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


我以为存在这样的东西。您可以链接到有关attr_changed的相关文档吗?和attr_was?
·霍尔洛姆贝

当然,我在答案中添加了链接。
路加·弗朗克

10

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

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

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

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



8

要获取所有更改的字段(分别具有旧值和新值):

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

5
我认为现在应该person.previous_changes改为最后一行,而不是person.changes
Jason Axelson


3

将此添加到您的模型:

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

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


0

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

要回答您的问题,您可以将一个成员变量设置为before_update中的旧值,然后可以在after_update中对其进行访问,但这不是一个很好的解决方案。


我正在努力查看如何使用事务实现我想要的东西。这样的交易是否存在于模型或控制器中?我是否要删除“ after_update”和“ before_update”回调?最后,如何获得确定差异所需的旧值?
亚伯

没关系,我看到我必须将代码放在控制器中。我们很好。
亚伯2009年


0

如果要在更新后获取特定字段的值,可以使用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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.