ActiveRecord的嵌套收集属性更改

时间:2012-06-21 09:58:43

标签: ruby-on-rails nested-attributes

我有一个用户模型字段列表(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

你能提供一些如何重构这种方法的建议吗?谢谢!

1 个答案:

答案 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])