1

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

你能建议如何重构这个方法吗?谢谢!

4

1 回答 1

0

这需要大量代码来完成 Rails 已经为您提供的功能。

我看到您已经在使用该changed?方法来检查属性与数据库中的当前值相比是否发生了变化。

但是,由于您使用update_attributes,更改会立即保存,因此跟踪更改变得更加困难。您可以在模型上使用before_save回调,它可以在更新之前跟踪某些内容是否发生了变化。

例如:

before_save :check_changed
def check_changed
  puts (changed? ? "changed" : "unchanged")
end

或者您可以修补 ActiveRecord 本身,以便在调用后返回更改的属性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])
于 2012-06-21T10:47:08.683 回答