Rails ActiveRecord 3.2:如何跳过子模型中的before_delete回调



我在Rails 3.2.16中工作。在我的应用程序中,一个帐户有许多用户。一个帐户必须总是有管理员用户,所以你不能销毁他们。

class Account < ActiveRecord::Base
  has_many :users, :dependent => :destroy
end
class User < ActiveRecord::Base
  before_destroy :check_if_admin
  def check_if_admin
    false if self.is_admin
  end
end

但是,当您销毁整个帐户时,管理员也应该被销毁。相反,当我从控制器调用@account.destroy时,User#before_delete回调会阻止admin用户被销毁。

我知道我可以调用@account.delete来跳过回调,但我的理解是:dependent => :destroy本身就是一个回调,因此只会删除帐户,而不会删除用户。

是否有一种方法可以在回调中知道我来自哪里,例如

def check_if_admin
  return if [I'm doing an Account dependent delete]
  false if self.is_admin
end

还是我必须在destroy之前手动delete用户?

在这个答案的帮助下,我找到了这个解决方案。这个想法是在销毁父回调的同时暂时关闭特定的子回调。请注意,我必须添加:prepend => :true选项才能将自定义回调重新添加到链的前面。

class Account < ActiveRecord::Base
  before_destroy :disable_user_check_if_admin
  before_destroy :enable_user_check_if_admin
  has_many :users, :dependent => :destroy
  def disable_user_check_if_admin
    User.skip_callback(:destroy, :before, :check_if_admin)
  end
  def enable_user_check_if_admin
    User.set_callback(:destroy, :before, :check_if_admin), :prepend => :true
  end
end
class User < ActiveRecord::Base
  before_destroy :check_if_admin
  has_many :contacts, :dependent => :restrict
  def check_if_admin
    false if self.is_admin
  end
end

没有:prepend => :true,我遇到了麻烦,因为我的用户模型也是has_many :contacts, :dependent => :restrict。问题是,skip_callback实际上删除了回调,而set_callback在回调链的末尾重新添加了回调。使用:prepend => :true,我能够在链的前面插入我的自定义before_destroy :check_if_admin回调。查看文档和源代码。

备选方案(未使用)

当我与回调序列作斗争时,我尝试了一个不同的解决方案,使回调保持不变。借鉴这个答案,我在Account上使用了一个访问器来检查它何时被删除:

class Account < ActiveRecord::Base
  before_destroy :disable_user_check_if_admin
  before_destroy :enable_user_check_if_admin
  has_many :users, :dependent => :destroy
  attr_accessor :destroying
  def disable_user_check_if_admin
    self.destroying = true
  end
  def enable_user_check_if_admin
    self.destroying = false
  end
end
class User < ActiveRecord::Base
  before_destroy :check_if_admin
  has_many :contacts, :dependent => :restrict 
  def check_if_admin
    return if self.account.destroying
    false if self.is_admin
  end
end

这确实有效,但它不"闻"正确设置和检查这样的标志,所以我回到使用:prepend => :true的跳过/设置回调。

相关内容

  • 没有找到相关文章

最新更新