【发布时间】:2011-06-30 09:13:16
【问题描述】:
需要在 Rails 3 中更新之前检查属性块是否已更改。
街道 1、街道 2、城市、州、邮政编码
我知道我可以使用类似的东西
if @user.street1 != params[:user][:street1]
then do something....
end
但是那段代码真的很长。有没有更清洁的方法?
【问题讨论】:
标签: ruby-on-rails ruby-on-rails-3
需要在 Rails 3 中更新之前检查属性块是否已更改。
街道 1、街道 2、城市、州、邮政编码
我知道我可以使用类似的东西
if @user.street1 != params[:user][:street1]
then do something....
end
但是那段代码真的很长。有没有更清洁的方法?
【问题讨论】:
标签: ruby-on-rails ruby-on-rails-3
查看ActiveModel::Dirty(默认情况下适用于所有型号)。该文档非常好,但它可以让您执行以下操作:
@user.street1_changed? # => true/false
【讨论】:
这就是我解决检查多个属性变化的问题的方法。
attrs = ["street1", "street2", "city", "state", "zipcode"]
if (@user.changed & attrs).any?
then do something....
end
changed 方法返回为该对象更改的属性数组。
@user.changed 和 attrs 都是数组,所以我可以得到交集(参见 ary & other ary 方法)。交集的结果是一个数组。通过在数组上调用any?,如果至少有一个交叉点,我会得到正确的结果。
同样非常有用的是,changed_attributes 方法返回属性的哈希值及其原始值,changes 返回属性的哈希值及其原始值和新值(在数组中)。
您可以查看 APIDock 以了解哪些版本支持这些方法。
【讨论】:
if attrs.any?{|attr| @user.send("#{attr}_changed?")} 当我想检查几个不同属性中的一个是否发生了变化时——当然,我只对我自己定义的 attrs 执行此操作,因为我不喜欢将用户参数放入send 方法。 ;)
previously_changed适配,一般在after_commit:if (@user.previous_changes.keys & attrs).any?
ActiveModel::Dirty 对我不起作用,因为 @model.update_attributes() 隐藏了更改。所以这就是我在控制器的 update 方法中检测到的变化:
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
如果您尝试检测大量属性更改,但它可能会变得混乱。可能不应该在控制器中这样做,但是嗯。
【讨论】:
previous_changes 执行此操作,默认情况下也可用。
#update_attributes 不会隐藏更改。它保存记录,因此模型已更新并且没有更改。您需要更改模型中的字段并在保存之前检查changed?。 IE。 @model.field = 'foo' 或 @model.attributes = @model.attributes.merge(params[:model])
以上答案更好,但对于知识,我们还有另一种方法, 让对象的“类别”列值更改(@design),
@design.changes.has_key?('catagory')
.changes 将返回一个散列,其中键作为列名,值作为数组,每列有两个值 [old_value, new_value]。例如上面的类别从@design的'ABC'更改为'XYZ',
@design.changes # => {}
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }
【讨论】:
对于 rails 5.1+ 回调
从 Ruby on Rails 5.1 开始,attribute_changed? 和 attribute_was ActiveRecord 方法将被弃用
使用saved_change_to_attribute? 代替attribute_changed?
@user.saved_change_to_street1? # => true/false
更多例子here
【讨论】: