Prevent STI when inheriting from an ActiveRecord model

10,736

Solution 1

You can achieve this by disabling the inheritance_column for the model, like so:

class AnotherSection < Section
  # disable STI
  self.inheritance_column = :_type_disabled

end

Solution 2

The accepted answer will definitely work, but the recommended (dare I say "proper" :) way is to set abstract_class:

class Section < ActiveRecord::Base
  self.abstract_class = true
end

Solution 3

The only fully supported strategy to store inheritance on ActiveRecord is STI. You can, however, simulate concrete class-table inheritance at your own risk. The concrete class-table inheritance with abstract superclass works fine, as pointed by smathy.

BUT ... If what you want is to make AnotherSection just an ordinary class (that will not be persisted at the database), you could disable the discriminator column (as suggested by Veraticus). However, if you save the AnotherSection it will be persisted in the same table as Section, and you will not be able to tell them apart. Also, if you use AnotherSection to find a Section, it will return an AnotherSection, breaking the original instantiation:

    #create a Section and saves it
    sect = Section.create()
    sect.save() 
    #retrieve the Section as a AnotherSection, breaking polymorphism... 
    sect = AnotherSection.find(sect.id)
    # another section is more than a section, it is inconsistent.

If AnotherSection is not intended to be persisted, the safest path it to override the persistence operations, such as save() and find():

    class AnotherSection < Section
       # disable STI, as pointed by Veraticus
       self.inheritance_column = :_type_disabled
       # disable save and finding
       def save(*args)
         #exception? do nothing?
       end
       def find(*args)
         #exception? do nothing?
       end
       def find_by(*args)
         #exception? do nothing?
       end
       # this does not stops here! there is first, last, and even a forty_two finder method! not to mention associations...
    end

in a nutshell, you can do this, but you SHOULDN´T. The risk is high. You should consider another option, such as using MIXIN instead of inheritance.

Share:
10,736
Mike
Author by

Mike

Updated on June 07, 2022

Comments

  • Mike
    Mike about 2 years

    On Rails 3.2.6, I have a class that inherits from ActiveRecord::Base:

    class Section < ActiveRecord::Base
      ...
    end
    

    When I inherit from this class, Rails will assume I want STI:

    class AnotherSection < Section
       ..Rails assumes I have a type field, etc...
    end
    

    I want to be able to inherit from the Section class and use the subclass as a normal Ruby subclass, without the Rails STI magic.

    Is there a way to prevent STI when subclassing from an ActiveRecord::Base model?