覆盖rails活动记录会破坏has_and_belongs_to_many关系的意外删除

时间:2012-12-02 02:47:33

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

我有一个继承自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表中的记录。

知道为什么会这样,以及如何阻止它?

2 个答案:

答案 0 :(得分:5)

您实际上不必覆盖Commentable模型上的destroy,只需添加一个返回before_destroy的{​​{1}}回调来实际取消销毁调用。例如:

false

class Commentable < ActiveRecord::Base # ... some code ... before_destroy { |record| comments.destroy_all self.deleted = true self.save! false } # ... some code ... end 模型也是如此;只需添加回调而不覆盖destroy方法本身。

有关可用回调的更多内容为here

答案 1 :(得分:0)

如果返回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之前,从falsebefore_ callback中的任何ActiveModel or ActiveModel::Validations, ActiveRecord返回ActiveSupport导致回调链停止。

我们可以通过将配置更改为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

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