【问题标题】:Validate Before Destroy销毁前验证
【发布时间】:2011-07-28 01:54:00
【问题描述】:

我有三个班级: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

现在,我希望在 Administratorship 的实例上调用 destroy 时,它会向模型添加错误并防止模型被破坏。我已经删除了unless 语句,看看这是否阻止了错误被添加,但事实并非如此。似乎模型上有错误并不能阻止破坏的发生。

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

【问题讨论】:

  • 不应该是> 1吗?这个查询不是在删除发生之前执行的吗?
  • @panzi 它正在计算任何没有当前管理员帐户 ID 的管理员

标签: ruby-on-rails validation


【解决方案1】:

如果你从那个 before_destroy 方法返回 false,它将阻止销毁。

【讨论】:

  • 对于您希望对象在销毁之前处于有效状态的一般情况,您可以这样做:before_destroy :valid?
  • 如果立即返回,似乎会在 Rails 4.1 中引发 LocalJumpError: unexpected return,请参阅 github.com/rails/rails/issues/12981 '#Fix 1'
  • 如果有人在这里偶然发现 Rails 5,返回 false 不再是方法。请改用 throw(:abort),就像在 Martin Cabrera Diaubalick 的回答中一样。
【解决方案2】:

从您的验证方法中返回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 => 'new'。在这种情况下,有没有删除视图)。发生这种情况时,设置错误消息的实例变量(在errors.add(:base,"message")中)实际上正在被重置,这会破坏过程中的错误。

【讨论】:

  • 有什么办法可以解决您在旁注中提出的问题?
  • 要解决旁注中提出的问题,请在相关控制器的销毁操作中的redirect_to 行中添加类似以下内容:format.html { redirect_to products_url, :notice => "An Error Occurred! #{@products.errors[:base].to_s}" }。然后,只要您在应用程序的某处显示 Flash 消息(例如 application.html.erb),它就会出现。见this guidethis question
【解决方案3】:

我最终使用此处的代码在 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 上的删除按钮变得微不足道

【讨论】:

  • 如何保存?我几乎使用了您的解决方案,但使用dependent: :restrict_with_error 解决了。但我仍然在想您的解决方案将如何节省......
【解决方案4】:

这是一个 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

【讨论】:

  • 在 Rails 5.1.4 中,我什至没有看到弃用警告。
  • @MarkFraser 在 5.0.0 之后删除了很多弃用警告,并改为实施
  • 谢谢!有用! return falseNO 是一个解决方案。
【解决方案5】:

对于 Rails 5,返回false 不会停止回调链。你需要使用throw(:abort)

belongs_to :帐户 归属地:学校

before_destroy :confirm_presence_of_alternate_administratorship_in_school

受保护

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    相关资源
    最近更新 更多