Rails 4 after_save previous_changes不起作用

时间:2016-02-01 21:14:01

标签: ruby-on-rails ruby-on-rails-4 activerecord awesome-nested-set

我在模型上有一个after_save回调,我正在调用previous_changes来查看属性(is_complete)是否发生了变化。即使属性发生更改,previous_changes也会返回空哈希值。

这是回调:

after_save do |record|
  puts "********************"
  puts record.previous_changes.to_s
  puts record.is_complete
  puts "********************"
end

以下是我在日志中的内容:

********************
{}
true
********************
********************
{}
false
********************

如果is_complete的值从true更改为false,则它应该在previous_changes哈希中。更新正在通过正常保存完成!而且我没有重新加载这个对象。

--- 更新 ---

当我发布问题时我没有考虑过这个问题,但我的模型使用了awesome_nested_set gem,看来这是重新加载对象或以某种方式干扰after_save回调。当我注释掉acts_as_nested_set时,回调似乎工作正常。

--- 更新2 ---

使用around_save回调修复此问题,该回调首先确定属性是否已更改,然后生成,然后执行在数据库中进行更改后需要执行的操作。工作解决方案如下所示:

around_save do |record, block|
  is_complete_changed = true if record.is_complete_changed?
  block.call
  if is_complete_changed
    ** do stuff **
  end
end

2 个答案:

答案 0 :(得分:5)

根据ActiveModel::Dirty源代码

从第274行

 def changes_applied # :doc:
    @previously_changed = changes
    @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
 end

因此,在调用@previously_changed后,更改将设置为changes_applied,并且在调用changes_apply时调用save,这意味着AFTER DOING PERSISTENT WORK(行42)

总之,previous_changes仅在记录实际保存到持久存储(DB)时具有值

所以在你的回调中,你可以使用record.changed_attributes,而在外面使用previously_changed,它会正常工作!

答案 1 :(得分:2)

我没有深挖,但从第一眼看到ActiveModel::Dirtycan see,方法previous_changes

def previous_changes
  @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
end

@previously_changed未在任何地方定义(except for here,它使用我在下面提到的changes方法),因此您获得了空(虽然很好并且具有无差别的访问权限:D)哈希一直以来。

您真正想要使用的是changes方法:

def changes
  ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
end

它将返回您的预期

#=> {"is_complete"=>[true, false]}