In this ruby on rails tutorial, we will discuss about single table inherintance. It is one of the implementation of object oriented inheritance in relational database. While mapping the database table with the object in object oriented language, one of the field in the database table identifies what class in the inheritance hierarchy it belongs to. In this process there exists different classes, one being the parent and other being the child classes inheriting from the same parent class. The inherited class can thus also override the default behaviour of the class. Single table inheritance or STI in short, is very famous in object oriented languages like Ruby and Java. All of the fields of classes attribute are stored in the same table in this process so called as single table inheritance.
We create a one table in database and it is more like self referral association. All of the objects in the column in the database table has same data type. Now let’s take an example say we have a class called User
|class User < ActiveRecord::Base
And we have two methods can_create_new_user? and delete_comments_of_other_users? Both of which returns false. This is the default for the standard user. But now let’s imagine we have another two special types of user. Say Admin and Advertiser. They both inherit from User class.
|class Admin < User
And another class
|class Advertiser < User
So here we have different classes with same data but the behaviour they show is different. Notice that both of these class are inheriting from User class and not from ActiveRecord or ApplicationRecord. So they get the methods defined in our User class can_create_new_user? and delete_comments_of_other_user? however we are defining the methods again to override the default inheritance. Now let’s see what a standard user has data fields.
Now we need to add one more field in this table so that we can identify what type of the user is creating the comments.
In the type column of the table, we store the name of the class. So now we have the same data and Rails will have the indicator to which class to use for the object. This type column in Ruby On Rails is magical, and when you add this column to the database table, Rails will know to use the Single Table Inheritance. Now the following queries will work and rails will do all the magical things behind the scene for us to identify what table to use
|user = User.all
# select * from User
admin = Admin.all
# This should be like user = User.where(:type => “Admin”) but rails does this for us behind the scene and we can use the command above
advertiser = Advertiser.all
# This should be like user = User.where(:type => “Advertiser”) but rails does this for us behind the scene and we can use the command above like earlier scene.
And creating a new user with type admin is same as creating a new admin
|user = User.create(:type => “Admin”, :username => “Rabin”)
# is same as
admin = Admin.create(:username => “Rabin”)
So Rails manages type behind us as it recognizes it as single table inheritance.
Now let’s use this in our Rails Model so that we can figure out how to use it.
Say we have a online shopping website and we have products table in database that stores the description about products. We have different types of products like digital products and virtual products. So now let’s add the type column to our database table of products so that we can specify what kind of products are.
|$ rails g migration AddTypeToProducts type:string|
Now we also need to generate our models that inherit from the parent class Product.Let’s generate model for DigitalProducts
|$ rails g model DigitalProduct –parent=Product|
The –parent=Product flag tells Rails not to generate migration and inherit the behaviour from the Product class and not from ActiveRecord. Let’s do the same for VirtualProduct
|$ rails g model VirtualProduct –parent=Product|
Now, say we already have a lot of products in our database and we want to make all of their type to be DigitalProduct when we run the migration so let’s tweak our migration file
|class AddTypeToProducts < ActiveRecord::Migration[5.0]
add_column :products, :type, :string
Product.update_all(“ type = ‘DigitalProduct’ “)
Save this and run the migration
|$ rails db:migrate|
Now you can create more methods in our products model and we can inherit from it. After migration you will have the type field in the products table with the default value being DigitalProduct. You can now use ActiveRecord query to check this out and change or play with it to see if that works for your case.
When to not use single table inheritance(STI)?
Single table inheritance works best when we have the attributes in common and the behaviour different. This is not suitable when we have different attributes in our model and we have different behaviours. So in such case we need to use the join table to join the database tables and use that for modeling our relation in the database.
If there exists a clear object oriented inheritance style hierarchy in our database model. If not then this process becomes very difficult to maintain.
Also we need to make sure we don’t want much data attributes in our child class that are non-global, that will make our database messy.
We also need to figure it out that do we really need to store all the values in a single table or not? If not then it is always good idea to separate the products to different tables.
So single table inheritance is one of the powerful feature that is provided by Rails. Before using this feature we need to answer the following questions
- Do I need to store items in just a single table?
- Do I need to add more attributes in child class?
- Is the relation between classes inheritable? etc.
Want some more tutorial? click here if you like to know about Hash and Symbols in Ruby on Rails Tutorial