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

给定一个模型

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

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

 class DerivedModel < BaseModel end 

DerivedModel将以良好的OO方式inheritanceBaseModel所有行为,包括validates_presence_of :parent_id 。 我想关闭DerivedModel的validation,并防止回调方法被触发,最好不要修改或以其他方式破坏BaseModel

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

我喜欢使用以下模式:

 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方法来解决这个问题,那将会很好,但是这种模式可以很好地处理复杂的交互。

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

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

这种方法的好处是您不需要为在Child类中禁用validation所需的每个列名创建多个方法。

从源代码中探索(我目前在rails 1.2.6上),回调相对简单。

事实certificate, before_validation_on_createbefore_save等方法,如果没有用任何参数调用,将返回包含分配给该’回调站点’的所有当前回调的数组

要清除before_save,你可以简单地做

 before_save.clear 

它似乎工作

一个更清洁的方式是这个:

 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 ,则完成唯一性validation。

  1. Child实例类是Child ,与Parent不同。
  2. Parent Instance类是Parent ,与Parent相同。

再次在源代码中探讨,似乎可以在每次保存或仅更新/创建时运行validation。 这映射到

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

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

 write_inheritable_attribute :validate, nil 

这是我在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 

到目前为止,它对我来说一直很好。

从rails 3.0开始,您还可以访问validators 类方法来操作获取所有validation的列表。 但是,您无法通过此Array操纵该组validation。

至少从rails 5.0开始,你似乎能够操纵_validators (未记录的)方法。

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

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

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