【问题标题】:overriding rails active record destroy unexpected deletion of has_and_belongs_to_many relationships覆盖 Rails 活动记录会破坏 has_and_belongs_to_many 关系的意外删除
【发布时间】:2012-12-02 02:47:32
【问题描述】:

我有一个继承自 ActiveRecord::Base 的 Commentable 类和一个继承自 Commentables 的 Event 类。

我已经覆盖了这两个类中的destroy方法,并且Event.distroy调用了super。然而,一些意想不到的事情发生了。具体来说,事件的 has_and_belongs_to_many 关联被删除。我认为这是因为某些模块被包含在 Commentables 和 Event 类之间,但不确定是否有办法阻止这种情况。

这是简化的代码:

class Commentable < ActiveRecord::Base
  has_many :comments

  def destroy
    comments.destroy_all
    self.deleted = true
    self.save!
  end

end

class Event < Commentable
  has_and_belongs_to_many :practitioners, :foreign_key => "commentable_id"

  def destroy
    #some Event specific code
    super
  end

end

我不想从数据库中删除行,只需设置一个“已删除”标志。我也不想删除任何关联。但是,在 Event.destroy 和 Commentable.destroy 之间的某个地方,其他一些 rails 代码会破坏 has_and_belongs_to_many 表中的记录。

知道为什么会发生这种情况以及如何阻止它吗?

【问题讨论】:

    标签: ruby-on-rails activerecord inheritance super destroy


    【解决方案1】:

    您实际上不必在Commentable 模型上覆盖destroy,只需添加一个返回falsebefore_destroy 回调即可实际取消destroy 调用。例如:

    class Commentable < ActiveRecord::Base
     # ... some code ...
      before_destroy { |record|
        comments.destroy_all
        self.deleted = true
        self.save!
        false
      }
     # ... some code ...
     end
    

    Event 模型也是如此;只需添加一个回调而不覆盖destroy方法本身。

    更多关于可用回调的信息是here

    【讨论】:

      【解决方案2】:

      如果返回 false,Rails 5 不会停止回调链。我们将不得不改用throw(:abort)

      before_destroy :destroy_validation
      
      def destroy_validation
        if condition
          errors.add(:base, "item cannot be destroyed because of the reason...")
          throw(:abort)
        end
      end
      

      在 Rails 5 之前,从 ActiveModel or ActiveModel::Validations, ActiveRecordActiveSupport 中的任何 before_ callback 返回 false 会导致回调链停止。

      我们可以通过将此配置更改为true 来关闭此默认行为。但是,当从回调返回 false 时,Rails 会显示弃用警告。

      新的 Rails 5 应用程序提供了一个名为 callback_terminator.rb 的初始化程序。

      ActiveSupport.halt_callback_chains_on_return_false = false
      

      默认值设置为false

      => DEPRECATION WARNING: Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in the next release of Rails. To explicitly halt the callback chain, please use `throw :abort` instead.
      ActiveRecord::RecordNotSaved: Failed to save the record
      

      这是一个受欢迎的更改,有助于防止回调意外停止。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多