代码之家  ›  专栏  ›  技术社区  ›  stellard

过载和绕过活动记录更新

  •  0
  • stellard  · 技术社区  · 15 年前

    我正试图在我的应用程序中实现一个审计跟踪,但是由于一些需求,我无法使用任何现有的gems或插件。

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

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

    现在,我已经重载了创建\或\更新以获取功能的第一部分

    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 回复  |  直到 15 年前
        1
  •  1
  •   Josh Delsman    15 年前

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

    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_访问器,比如说“perform_updates”,在默认情况下,这当然是零。

    如果要对数据库执行更新,首先必须将属性设置为true,然后运行更新。否则,更新将只创建一个新的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 回调它所属的位置。一旦在某些接口中批准了更新,那么观察者就可以执行更新,覆盖当前的内容,直到批准了所做的另一个更改。如果队列中的某个对象当前有要审批的更新,则可以通知用户更改正在等待审批等。