Ruby-on-rails 销毁前验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5520320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:50:04  来源:igfitidea点击:

Validate Before Destroy

ruby-on-railsvalidation

提问by tanman

I have three classes: School, Account, and Administratorship.

我有三个类:SchoolAccount,和Administratorship

School

学校

has_many :administatorships
has_many :administrators, :through => :administratorships

Account

帐户

has_many :administratorships

Administratorship

管理权

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

Now, what I would like to happen is when I call destroyon an instance of Administratorship, for it to add an error to the model and prevent the destruction of the model. I have removed the unlessstatement to see if that was preventing the error from being added, but it wasn't the case. It seems that having errors on the model does not prevent the destroy from occurring.

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

So my question is, is there any way I can prevent the destroy from occurring using validations? I realize I could define a method that destroys only if the above condition is met, but it seems that a validation approach is a more elegant solution.

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

回答by

If you return false from that before_destroy method, it will prevent the destruction.

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

回答by Martin Cabrera Diaubalick

This is a Rails 5answer, if you return false it will give a deprecation warning: "Returning falsein Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1".

这是Rails 5 的答案,如果您返回 false,它将给出弃用警告:“Returning falsein Active Record 和 Active Model 回调不会隐式停止 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

回答by Ryan

Returning falsefrom your validation method will prevent the record from getting destroyed.

false从您的验证方法返回将防止记录被破坏。

Example:

例子:

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

Side note:I was having trouble with this error message not being displayed. The validation would work and the object would not be deleted, but there would be no message letting me know what happened. The reason for this was that the controller was redirecting to the index view instead of rendering the delete view (if there is an error while creating a new user for example, it will render :action => 'new'. In this case there is no delete view). When this happened, the instance variable on which the error message was set (in errors.add(:base,"message")) is actually being reset, which destroys the error in the process.

旁注:我遇到了未显示此错误消息的问题。验证会起作用并且对象不会被删除,但不会有任何消息让我知道发生了什么。这样做的原因是控制器重定向到索引视图而不是呈现删除视图(例如,如果在创建新用户时出现错误,它将呈现 :action => 'new'。在这种情况下有没有删除视图)。发生这种情况时,设置错误消息的实例变量(在 errors.add(:base,"message") 中)实际上正在重置,这会破坏进程中的错误。

回答by Hugo Forte

I ended up using code from here to create a can_destroy override on activerecord: https://gist.github.com/andhapp/1761098

我最终使用这里的代码在 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

This has the added benefit of making it trivial to hide/show a delete button on the ui

这有一个额外的好处,即在 ui 上隐藏/显示删除按钮变得微不足道

回答by Ndeto

For Rails 5, returningfalsewon't halt the callback chain. You need to use throw(:abort)

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

belongs_to :account belongs_to :school

归属地:账户归属地:学校

before_destroy :confirm_presence_of_alternate_administratorship_in_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"
        throw(:abort)
    end
end