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

安稳与你 提交于 2019-12-04 16:52:36

问题


Given a model

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

and a derived model (the underlying database table has a type field - this is simple rails STI)

class DerivedModel < BaseModel
end

DerivedModel will in good OO fashion inherit all the behaviour from BaseModel, including the validates_presence_of :parent_id. I would like to turn the validation off for DerivedModel, and prevent the callback methods from firing, preferably without modifying or otherwise breaking BaseModel

What's the easiest and most robust way to do this?


回答1:


I like to use the following pattern:

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

It would be nice if rails provided a skip_validation method to get around this, but this pattern works and handles complex interactions well.




回答2:


As a variation of the answer by @Jacob Rothstein, you can create a method in parent:

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

class Child < Parent
end

The benefit of this approach is you not need to create multiple methods for each column name you need to disable validation for in Child class.




回答3:


From poking around in the source (I'm currently on rails 1.2.6), the callbacks are relatively straightforward.

It turns out that the before_validation_on_create, before_save etc methods, if not invoked with any arguments, will return the array which holds all the current callbacks assigned to that 'callback site'

To clear the before_save ones, you can simply do

before_save.clear

and it seems to work




回答4:


A cleaner way is this one:

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


class Child < Parent
end

Or you can use it also in this way:

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

So, uniqueness validation is done if the instance class of model is Parent.

  1. Instance class of Child is Child and differs from Parent.
  2. Instance class of Parent is Parent and is the same as Parent.



回答5:


Since rails 3.0 you can also access the validators class method to manipulate get a list of all validations. However, you can not manipulate the set of validations via this Array.

At least as of rails 5.0 you however seem to be able to manipulate the _validators (undocumented) method.

Using this method you can modify the validations in the subclass like e.g.:

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

While this uses an undocumented method, is has the benefit of not requiring the superclass to know anything about the child's implementation.




回答6:


Again poking around in the source, it seems that validations can be run either on every save, or updates/creates only. This maps to

:validate => all saves
:validate_on_create => creations only
:validate_on_update => updates only

To clear them, you can use write_inheritable_attribute, like this:

write_inheritable_attribute :validate, nil



回答7:


Here is a slight variation of RubyDev's that I've been using in mongoid 3.

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

class Child < Parent
end

It has been working pretty good for me so far.



来源:https://stackoverflow.com/questions/279478/how-can-i-disable-a-validation-and-callbacks-in-a-rails-sti-derived-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!