我有三个课程:School
,Account
和Administratorship
。
学校
has_many :administatorships
has_many :administrators, :through => :administratorships
帐户
has_many :administratorships
管理人的职位
belongs_to :account
belongs_to :school
before_destroy :confirm_presence_of_alternate_administratorship_in_school
protected
def confirm_presence_of_alternate_administratorship_in_school
unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0
errors.add_to_base "The school must have at least one administrator"
end
end
现在,我想要发生的是当我在destroy
的实例上调用Administratorship
时,它会向模型添加错误并防止模型被破坏。我删除了unless
语句,看看是否阻止了错误的添加,但事实并非如此。似乎模型上的错误并不能防止破坏发生。
所以我的问题是,有什么方法可以防止使用验证发生破坏?我意识到我可以定义一个仅在满足上述条件时才会破坏的方法,但似乎验证方法是更优雅的解决方案。
答案 0 :(得分:64)
如果从before_destroy方法返回false,则会阻止破坏。
答案 1 :(得分:24)
这是 Rails 5 答案,如果您返回false,则会给出弃用警告:“在Active Record和Active Model回调中返回false
不会隐式停止Rails 5.1中的回调链“。
def confirm_presence_of_alternate_administratorship_in_school
return if school.administrators.count(["administratorships.account_id != #{id}"]) > 0
errors[:base] << 'The school must have at least one administrator'
throw :abort
end
答案 2 :(得分:15)
从验证方法返回false
将阻止记录被破坏。
示例:强>
def confirm_presence_of_alternate_administratorship_in_school
unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0
# errors.add_to_base() is deprecated in Rails 3. Instead do...
errors.add(:base, "The school must have at least one administrator")
# this will prevent the object from getting destroyed
return false
end
end
旁注:我无法显示此错误消息。验证将起作用,对象不会被删除,但没有消息让我知道发生了什么。原因是控制器重定向到索引视图而不是渲染删除视图(例如,如果在创建新用户时出现错误,它将呈现:action =&gt;'new'。在这种情况下,没有删除视图)。发生这种情况时,设置错误消息的实例变量(在errors.add(:base,“message”)中)实际上正在被重置,这会破坏过程中的错误。
答案 3 :(得分:1)
我最终使用此处的代码在activerecord上创建了一个can_destroy覆盖:https://gist.github.com/andhapp/1761098
class ActiveRecord::Base
def can_destroy?
self.class.reflect_on_all_associations.all? do |assoc|
assoc.options[:dependent] != :restrict || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?)
end
end
end
这样做的另一个好处是可以隐藏/显示ui上的删除按钮
答案 4 :(得分:1)
对于Rails 5,返回false
不会中断回调链。您需要使用throw(:abort)
属于:帐户 当属:学校
before_destroy:确认在校的替代管理权
受保护的
def confirm_presence_of_alternate_administratorship_in_school
unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0
errors.add_to_base "The school must have at least one administrator"
throw(:abort)
end
end