7

我在模型上有一个 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
4

2 回答 2

5

根据ActiveModel::Dirty源代码

从第 274 行

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

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

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

因此,在您的回调中,您可以使用record.changed_attributes,而在外部使用previously_changed,它会正常工作!

于 2016-02-01T21:41:42.650 回答
1

我没有深入挖掘,但从第一眼看到ActiveModel::Dirty可以看到,方法previous_changes

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

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

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

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

它会返回您的预期

#=> {"is_complete"=>[true, false]}
于 2016-02-01T21:34:20.110 回答