Prevent STI when inheriting from an ActiveRecord model
Asked Answered
A

3

36

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?

Abduct answered 26/7, 2012 at 19:2 Comment(2)
if you don't have a type column that shouldn't bother you... if you do have a type, then you can disable it by doing what @Veraticus said..Ledezma
In fact you still have STI: instances from both classes will be stored in the same table, what is the definition of STI (Single Table Inheritance). You just do not want to have a discriminator column (the "type"). However, how you will know if each record from sections is a plain section or a AnotherSection?Circumlunar
W
54

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
Winwaloe answered 26/7, 2012 at 19:10 Comment(4)
That, or any inexistent column suffices.Ledezma
self.inheritance_column = nil worked for me (but I tried it long time ago and it was rails 3.2)Liv
this disables the discriminator column. But both classes are stored at the same table, what is the definition of STI (Single Table Inheritance). You just removed the discriminator column, and ruby will not be able to decide what is the type of each stored record (you will need to decide when loading)Circumlunar
@Liv This seemed to work well when we wanted to use a column named type without using STI. Thanks.Leet
B
12

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
Broadway answered 17/6, 2015 at 23:55 Comment(4)
This is the correct approach, and it has been around since Rails 1.1.Propaganda
This is not working for me. I cannot instantiate any object of this model class... ( NotImplementedError: MyModel is an abstract class and cannot be instantiated. ). So please revise your answer @BroadwayKong
@Kong you can't instantiate an abstract class, it's abstract.Broadway
Thanks for the reply. Yeah, I just wanted to disable STI from a model that has a 'type' column and did not read the question above properly.Kong
C
1

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.

Circumlunar answered 21/1, 2017 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.