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.

Leave a Reply

Your email address will not be published. Required fields are marked *