重载和绕过活动记录更新

时间:2010-02-04 09:29:48

标签: ruby-on-rails activerecord overloading updatemodel

我正在尝试在我的应用程序中实现审计跟踪,但由于某些要求,我无法使用任何现有的宝石或插件。

我想转移任何将模型更新为自定义方法的正常尝试,该方法将所有更新保存在另一个单独的表中(称为更新)。

然后,应用程序使用更新表来实际执行更新。

现在我已经重载了create_or_update以获得功能的第一部分

def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create : create_updates
  result != false
end


class Update < ActiveRecord::Base
  belongs_to :updatable, :polymorphic => true

  after_create :update_model

  private

  def update_model
    self.updatable.update_attribute self.attribute, self.new_value #infinite loop
  end
end  

现在的问题是,当更新模型尝试实际执行更新时,这会导致无限循环。

我一直在查看rails核心源,找到绕过第一个功能的最佳位置。我希望在事务内部执行这些更新,但我不确定在活动记录堆栈中的确切位置是从哪里开始还是结束。我也不想开始攻击活跃的资源。

任何建议都将不胜感激。

1 个答案:

答案 0 :(得分:1)

您是否确实需要将属性保存在单独的表中,然后在管理员查看并批准后执行更新?如果是这种情况,您可能只想覆盖更新方法以执行以下操作:

def update(perform_updates = false)
  if perform_updates
    latest_approved_update = UpdateAuditor.first(:conditions => { :updatable_id => self.id, :updatable_type => self.class.name, :approved => true })
    self.attributes = latest_approved_update.attributes
    self.save
  else 
    UpdateAuditor.create(:updatable_id => self.id, :updatable_type => self.class.name, :attributes => self.attributes)
  end
end

更新:作者评论说他们希望能够将此模型应用于所有更新。为了实现这一点,你可以在模型中添加一个attr_accessor,比如说“perform_updates”,默认情况下这肯定是零。

如果要对数据库执行更新,首先必须将该属性设置为true,然后运行update。否则,更新将只创建一个新的UpdateAuditor记录,需要得到管理员的批准。

class Person < ActiveRecord::Base
  has_many :audits, :class_name => "UpdateAudit", :as => :auditable

  attr_accessor :perform_updates

  private

  def create_or_update
    raise ReadOnlyRecord if readonly?

    if new_record?
      result = create
      result != false
    else
      if perform_updates
        latest_approved_update = audits.approved.last

        if latest_approved_update
          self.attributes = latest_approved_update.attributes
          update
        else
          return false
        end
      else
        audits.create(:updated_attributes => self.attributes)
      end 
    end
  end
end

为了记录,我认为覆盖默认更新方法是一个危险的游戏,并且这种编程在它所属的before_update回调中更好。一旦在某个界面中批准了更新,则观察者可以执行更新,覆盖当前存在的更新,直到可以批准所做的另一个更改。如果队列中的对象当前有更新要批准,则可以提醒用户正在等待批准的更改等。