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

12,324

Solution 1

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.

Solution 2

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

Solution 3

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.

Solution 4

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.

Share:
12,324
Mitkins
Author by

Mitkins

I do .NET, C++, Ruby, Automated Testing, Running and Snowboarding@borland

Updated on June 22, 2022

Comments

  • Mitkins
    Mitkins almost 2 years

    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?

  • Leszek Andrukanis
    Leszek Andrukanis over 10 years
    Awareness of existing Child class in the Parent isn't the best approach. I'm trying to cope with it in other way.
  • VoodooChild92
    VoodooChild92 over 8 years
    Hey.. This is a great way you suggested. I am using a class from a gem, so I can't change the Parent class and the function "check_base". Can you suggest me a way where I add some sort of validation skip in the child class.
  • Sidhannowe
    Sidhannowe over 4 years
    Shorter: validate_uniqueness_of :column_name, :unless => "is_a? Child"