How to implement Single Table Inheritance in rails
One of the interview questions in Ruby on rails would be What is STI…? How would you implement it…?. But before looking for an answer, think Why STI…?
Why Single Table Inheritance…?
When there are similar columns for a table, it’s not a good practice to create multiple tables with same columns. The database schema needs to be robust and optimised. We can create a single table and have type column which would tell which type of row it is.
For Example, we have Employee table having columns employee_number
, name
, username
, password
, joining_date
and ctc
. These columns are to be present in the tables Developer, Recruiter and Manager as well. Here, we can use Single Table Inheritance where type column would tell us whether the employee is a Developer, Recruiter or a Manager.
In rails, a table is mapped to a model by ORM (Object Relational Mapping). We shall see how can we implement STI.
How to implement STI…?
Let’s consider the above example, let’s create a model Employee.
rails g model Employee employee_number:integer name:string username:string password:string joining_date:date ctc:string
class Employee < ApplicationRecord end create_table :employees do |t| t.integer :employee_number t.string :name t.string :username t.string :password t.date :joining_date t.string :ctc t.timestamps end
Add type
as a string to the employee table.
rails g migration AddTypeToEmployees
class AddTypeToEmployees < ActiveRecord::Migration[5.0] add_column :employees, :type, :string end
Inherit the Manager model with Employee. Similarly for Recruiter and Developer.
class Manager < Employee end class Recruiter < Employee end class Developer < Employee end
Note: rake db:migrate
goes without saying
Now, Employee.new
would give Employee object or row with type nil
. As Employee is the superclass whose columns are inherited.
Employee.new
=> Employee id: nil, employee_number: nil, name: nil, username: nil, password: nil, joining_date: nil, ctc: nil, created_at: nil, updated_at: nil, type: nil
Employee.create(employee_number: 456, name: 'John Cena', username: 'johncena', ctc: '12 LPA')
=> Employee id: 4, employee_number: 456, name: "John Cena", username: "johncena", password: nil, joining_date: nil, ctc: "12 LPA", created_at: "2017-11-03 18:26:19", updated_at: "2017-11-03 18:26:19", type: nil
Now, Manager.new
would give Manager object or row with type "Manager"
.
Manager.new
=> Manager id: nil, employee_number: nil, name: nil, username: nil, password: nil, joining_date: nil, ctc: nil, created_at: nil, updated_at: nil, type: "Manager"
Manager.create(employee_number: 123, name: 'Srinidhi', username: 'srini', ctc: '12 LPA')
=> Manager id: 1, employee_number: 123, name: "Srinidhi", username: "srini", password: nil, joining_date: nil, ctc: "12 LPA", created_at: "2017-11-03 18:20:52", updated_at: "2017-11-03 18:20:52", type: "Manager"
Similarly it would work fine for Recruiter and Developer.
Remember that type
column should be added only for STI implementation, otherwise rails would throw an exception.