Rails 4 after_save previous_changes不起作用

J P*_*ato 4 activerecord ruby-on-rails ruby-on-rails-4 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
Run Code Online (Sandbox Code Playgroud)

这是我在日志中得到的:

********************
{}
true
********************
********************
{}
false
********************
Run Code Online (Sandbox Code Playgroud)

如果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
Run Code Online (Sandbox Code Playgroud)

Hie*_*ham 5

根据ActiveModel :: Dirty源代码

从第274行

 def changes_applied # :doc:
    @previously_changed = changes
    @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
 end
Run Code Online (Sandbox Code Playgroud)

因此,这些变化将被设置为@previously_changed之后changes_applied被调用,并changes_apply为呼叫时save被调用,这意味着AFTER DOING PERSISTENT WORK(42行)

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

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


And*_*eko 1

我没有挖得太深,但从第一眼就ActiveModel::Dirty可以看到,在方法中previous_changes

def previous_changes
  @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
end
Run Code Online (Sandbox Code Playgroud)

@previously_changed没有在任何地方定义(除了这里,它使用changes我下面提到的方法),因此你总是得到空的(很好并且具有无关紧要的访问:D)散列。

你真正想要使用的是一种changes方法:

def changes
  ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
end
Run Code Online (Sandbox Code Playgroud)

它会返回你的预期

#=> {"is_complete"=>[true, false]}
Run Code Online (Sandbox Code Playgroud)