Rails 3检查属性是否已更改

Rails 3 check if attribute changed

在Rails 3中更新之前,需要检查属性块是否已更改。

street1,street2,city,state,zipcode

我知道我可以使用类似的东西

1
2
3
if @user.street1 != params[:user][:street1]
  then do something....
end

但这段代码真的很长。 有更干净的方式吗?


查看ActiveModel :: Dirty(默认情况下适用于所有型号)。文档非常好,但它可以让您执行以下操作:

1
@user.street1_changed? # => true/false


这就是我如何解决检查多个属性中的更改的问题。

1
2
3
4
5
attrs = ["street1","street2","city","state","zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

changed方法返回为该对象更改的属性数组。

@user.changedattrs都是数组,所以我可以得到交集(参见ary & other ary方法)。交集的结果是一个数组。通过在数组上调用any?,如果至少有一个交集,我会得到。

同样非常有用的是,changed_attributes方法返回属性的散列及其原始值,changes返回属性的散列及其原始值和新值(在数组中)。

您可以检查APIDock支持这些方法的版本。

http://apidock.com/rails/ActiveModel/Dirty


ActiveModel::Dirty对我不起作用,因为@model.update_attributes()隐藏了这些变化。所以这就是我在控制器中的update方法中检测到它的变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

如果你试图检测很多属性变化,它可能会变得混乱。可能不应该在控制器中这样做,但是meh。


对于rails 5.1+回调

改进这篇文章并完成其他答案

对于下一个rails版本(5.1之后)的回调,第一个答案将被弃用。
那么检查属性是否已更改的新方法是使用saved_change_to_attribute?

你可以做:

1
@user.saved_change_to_street1? # => true/false

您可以在此博客上看到更多示例


以上答案更好,但对于知识我们还有另一个approch,
让一个对象(@design)改变'catagory'列值,

1
@design.changes.has_key?('catagory')

.changes将返回一个哈希,其中key作为列的名称,值返回为一个数组,每列有两个值[old_value,new_value]。例如,上面的catagory从@design的'ABC'变为'XYZ',

1
2
3
@design.changes   # => {}
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }

对于ROR中的参考变化