如何在 rails STI 派生模型中禁用验证和回调?

How can I disable a validation and callbacks in a rails STI derived model?

提问人:Orion Edwards 提问时间:11/11/2008 最后编辑:Orion Edwards 更新时间:9/14/2017 访问量:13083

问:

给定一个模型

class BaseModel < ActiveRecord::Base
  validates_presence_of :parent_id
  before_save :frobnicate_widgets
end

和一个派生模型(底层数据库表有一个字段 - 这是简单的 rails STI)type

class DerivedModel < BaseModel
end

DerivedModel将以良好的面向对象方式继承 的所有行为,包括 .我想关闭 的验证,并防止回调方法触发,最好不要修改或以其他方式中断BaseModelvalidates_presence_of :parent_idDerivedModelBaseModel

最简单、最强大的方法是什么?

ruby-on-rails 验证 回调 单表继承

评论


答:

3赞 Orion Edwards 11/11/2008 #1

从在源代码中四处走动(我目前在 rails 1.2.6 上),回调相对简单。

事实证明,如果不使用任何参数调用,etc 方法将返回数组,该数组包含分配给该“回调站点”的所有当前回调before_validation_on_createbefore_save

要清除before_save,您可以简单地执行以下操作

before_save.clear

它似乎有效

0赞 Orion Edwards 11/11/2008 #2

再次在源代码中四处逛逛,似乎验证可以在每次保存时运行,也可以仅更新/创建。这映射到

:validate=>所有保存
=仅
>次创作 =仅>更新
:validate_on_create:validate_on_update

要清除它们,您可以使用 ,如下所示:write_inheritable_attribute

write_inheritable_attribute :validate, nil
49赞 Jacob Rothstein 3/28/2009 #3

我喜欢使用以下模式:

class Parent < ActiveRecord::Base
  validate_uniqueness_of :column_name, :if => :validate_uniqueness_of_column_name?
  def validate_uniqueness_of_column_name?
    true
  end
end

class Child < Parent
  def validate_uniqueness_of_column_name?
    false
  end
end

如果 rails 提供了一种skip_validation方法来解决这个问题,那就太好了,但这种模式可以很好地工作并处理复杂的交互。

8赞 Chandresh Pant 4/18/2012 #4

作为 @Jacob Rothstein 的答案的变体,您可以在 parent 中创建一个方法:

class Parent < ActiveRecord::Base
  validate_uniqueness_of :column_name, :unless => :child?
  def child?
    is_a? Child
  end
end

class Child < Parent
end

此方法的优点是,无需为需要在 Child 类中禁用验证的每个列名创建多个方法。

评论

0赞 Sidhannowe 10/16/2019
短:validate_uniqueness_of :column_name, :unless => "is_a? Child"
0赞 hadees 1/30/2013 #5

这是我在 mongoid 3 中使用的 RubyDev 的细微变化。

class Parent
  include Mongoid::Document
  validates :column_name , uniqueness: true, unless: Proc.new {|r| r._type == "Child"}
end

class Child < Parent
end

到目前为止,它对我来说效果很好。

评论

3赞 Leszek Andrukanis 11/21/2013
了解 Parent 中的现有 Child 类并不是最好的方法。我正试图以其他方式应对它。
2赞 phlegx 2/26/2015 #6

一个更简洁的方法是:

class Parent < ActiveRecord::Base
  validate :column_name, uniqueness: true, if: 'self.class == Parent'
end


class Child < Parent
end

或者您也可以通过以下方式使用它:

class Parent < ActiveRecord::Base
  validate :column_name, uniqueness: true, if: :check_base

  private

  def check_base
    self.class == Parent
  end
end


class Child < Parent
end

因此,如果模型的实例类为 。Parent

  1. 的实例类是 和 不同于 。ChildChildParent
  2. 的实例类 是 和 是 的相同。ParentParentParent

评论

1赞 VoodooChild92 8/27/2015
嘿。。这是您建议的好方法。我正在使用 gem 中的类,因此我无法更改 Parent 类和函数“check_base”。你能建议我一种方法,让我在子类中添加某种验证跳过。
2赞 ulferts 9/14/2017 #7

从 rails 3.0 开始,您还可以访问类方法来操作获取所有验证的列表。但是,您不能通过此数组操作验证集。validators

然而,至少从 rails 5.0 开始,您似乎能够操作(未记录的)方法。_validators

使用此方法,您可以修改子类中的验证,例如:

class Child < Parent
  # add additional conditions if necessary
  _validators.reject! { |attribute, _| attribute == :parent_id } 
end

虽然这使用了一种未记录的方法,但它的好处是不需要超类知道任何关于子类实现的信息。