我在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
的跳过/设置回调。