我有一个用户模型字段列表(User
belongs_to Location
和belongs_to Company
):
approval_fields = [:email, :location => [:first_name, :last_name], :company => [:name, :address]]
我想在尝试使用此代码更新记录时收集用户的所有更改:
user.update_attributes(params[:user])
我为此写了一段丑陋的代码:
# Collects changed fields and returns hash of changes:
# Example: approval_fields = [:email, :location => [:first_name, :last_name]]
# res = _collect_approval_changes(approval_fields)
# res # => {'email' => 'new_value@change.com',
# 'location_attributes' => {'first_name' => 'NewFirstName', 'last_name' => 'NewLastName'}}
def _collect_approval_changes(approval_fields)
changes = {}
approval_fields.each do |f|
if f.is_a?(Hash)
key = f.keys.first
next unless self.public_send(key) # skip this association if associated object is nil
changes["#{key}_attributes"] ||= {}
f[key].each do |v|
if self.public_send(key).public_send("#{v}_changed?")
changes["#{key}_attributes"][v.to_s] = self.public_send(key).read_attribute(v)
end
end
changes.delete("#{key}_attributes") if changes["#{key}_attributes"].blank?
else
changes[f.to_s] = self.read_attribute(f) if self.public_send("#{f}_changed?")
end
end
changes
end
你能提供一些如何重构这种方法的建议吗?谢谢!
答案 0 :(得分:0)
这是Rails已经为你做的事情的很多代码。
我发现您已使用changed?
方法check if an attribute has changed与数据库中的当前值进行比较。
但是,由于您使用update_attributes
,因此更改会立即保存,因此跟踪更改变得更加困难。您可以在模型上使用before_save
回调,可以在更新之前跟踪某些内容是否发生了变化。
例如:
before_save :check_changed
def check_changed
puts (changed? ? "changed" : "unchanged")
end
或者您可以patch ActiveRecord itself,在致电update_attributes
后返回更改后的属性:
module ActiveRecord
class Base
def update_attributes_changed(attributes)
self.attributes = attributes
changes = self.changes
return save, changes
end
end
end
status, changes = user.update_attributes_changed(params[:user])