在销毁之前验证

时间:2011-04-02 00:48:33

标签: ruby-on-rails validation

我有三个课程:SchoolAccountAdministratorship

学校

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语句,看看是否阻止了错误的添加,但事实并非如此。似乎模型上的错误并不能防止破坏发生。

所以我的问题是,有什么方法可以防止使用验证发生破坏?我意识到我可以定义一个仅在满足上述条件时才会破坏的方法,但似乎验证方法是更优雅的解决方案。

5 个答案:

答案 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