Single Table Inheritance (STI) 
Raul Pristopan 
1 / 22
Agenda 
1. Introduction 
2. What is STI? 
3. When should we use STI? 
4. How do we implement STI? 
5. Reasons why you shouldn't use STI 
6. Demo 
2 / 22
Introduction 
3 / 22
Introduction 
In the beginning everything always works. Things get complicated with 
time when you start adding new features. 
Some weeks ago I had to refactor some parts of a project and also add 
some new features to it. 
After some hours of reviewing the actual code I run into some problems: 
1. Same code in multiple places 
2. Same data stored in 2 or more tables 
create_table "users", :force => true do |t| 
... 
t.string "image_file_name" 
t.string "image_content_type" 
t.integer "image_file_size" 
t.datetime "image_updated_at" 
... 
4 / 22
Introduction 
create_table "activities", :force => true do |t| 
... 
t.string "image_file_name" 
t.string "image_content_type" 
t.integer "image_file_size" 
t.datetime "image_updated_at" 
... 
create_table "image_attachments", :force => true do |t| 
t.string "image_file_name" 
t.string "image_content_type" 
t.integer "image_file_size" 
t.datetime "image_updated_at" 
t.datetime "created_at", :null => false 
t.datetime "updated_at", :null => false 
t.integer "used_by_count", :default => 1 
t.text "original_url" 
end 
5 / 22
What is STI? 
STI is basically the idea of using a single table to reflect multiple models 
that inherit from a base model, which itself inherits from 
ActiveRecord::Base. 
In the database schema, sub-models are indicated by a single "type" 
column. 
In other words we have: 
a base class 
descendant classes 
6 / 22
When should we use STI? 
Should be considered when dealing with model classes that share much of 
the same functionality and data fields (granular control over extending or 
adding to each class individually) 
When you have duplicate code over and over for multiple tables (and not 
being DRY) 
STI permits you to use keep your data in a single table while writing 
specialized functionality. 
7 / 22
When should we use STI? 
Suppose you have three classes in your application which model similar 
things. To make this easier to think about I’ll be referring to some hypothetical 
classes by name: Business and Employee. Let’s consider three choices for 
modeling this situation: 
1. Polymorphic Associations (separate classes, multiple tables) 
2. Single Table Inheritance (separate classes, one table) 
3. Single Class with conditionals (one class, one table) 
8 / 22
When should we use STI? 
Polymorphic Associations (separate classes, multiple tables) 
With Polymorphic Associations we use modules to share code among 
classes. 
We have the following active record models for a “Business” and an 
“Employee”. 
class Business < ActiveRecord::Base 
# Methods, variables and constants 
end 
class Employee < ActiveRecord::Base 
# Methods, variables and constants 
end 
9 / 22
When should we use STI? 
Polymorphic Associations (separate classes, multiple tables) 
For the contact information, we have the following active record models: 
class PhoneNumber < ActiveRecord::Base 
# Methods, variables and constants 
end 
class Website < ActiveRecord::Base 
# Methods, variables and constants 
end 
10 / 22
When should we use STI? 
Polymorphic Associations (separate classes, multiple tables) 
Now, for the associations, Business and Employee models can have many 
phone numbers and websites. And conversely, Phone Number/Website can 
belong to either a Business model or an Employee model. 
class Business < ActiveRecord::Base 
has_many :phone_numbers, :as => phonable 
has_many :websites, :as => webable 
end 
class Employee < ActiveRecord::Base 
has_many :phone_numbers, :as => phonable 
has_many :websites, :as => webable 
end 
11 / 22
When should we use STI? 
Polymorphic Associations (separate classes, multiple tables) 
class PhoneNumber < ActiveRecord::Base 
belongs_to :phonable, :polymorphic => true 
end 
class Website < ActiveRecord::Base 
belongs_to :webable, :polymorphic => true 
end 
Having the above setup, you can get a collection of phone numbers for a 
business instance via the call “@business.phone_numbers. 
Similarly, you can get a collection of phone numbers for an employee instance 
via “@employee.phone_numbers”. 
12 / 22
When should we use STI? 
Single Class with conditionals (one class, one table) 
A Single Class is not exactly a design pattern, or anything particularly 
interesting. 
I’m thinking of a model with a type-like attribute (maybe called kind) and 
some if statements in methods where you need different behavior for 
different kinds of objects. 
13 / 22
When should we use STI? 
Questions to ask yourself 
When deciding how to design your data models, here are some questions to 
ask yourself: 
1. Are the objects, conceptually, children of a single parent? 
2. Do you need to do database queries on all objects together? 
3. Do the objects have similar data but different behavior? 
14 / 22
How do we implement STI? 
Create the base class 
Model: Employee 
Attributes: 
Name - String 
Age - Integer 
Department - String 
$ rails generate model Employee name:string age:integer department:string 
# /app/models/employee.rb 
class Employee < ActiveRecord::Base 
# Methods, variables and constants 
end 
15 / 22
How do we implement STI? 
Add a :type attribute as a string to the base class 
$ rails generate migration add_type_to_employee type:string 
16 / 22
How do we implement STI? 
Create any necessary descendant classes 
# /app/models/developer.rb 
class Developer < Employee 
# Methods, variables and constants 
end 
# /app/models/tester.rb 
class Tester < Employee 
# Methods, variables and constants 
end 
17 / 22
Reasons why you shouldn't use STI 
It creates a cluttered data model 
Why don’t we just have one table called objects and store everything as 
STI? 
STI tables have a tendency to grow and expand as an application develops, 
and become intimidating and unweildy as it isn’t clear which columns 
belong to which models. 
18 / 22
Reasons why you shouldn't use STI 
It forces you to use nullable columns 
If sub-classes that you intend to use for STI have many different data 
fields, then including them all in the same table would result in a lot of 
null values and make it difficult to scale over time. In this case, you may 
end up with so much code in your model sub-classes that the shared 
functionality between sub-classes is minimal and warrants separate 
tables. 
A comic book must have an illustrator, but regular books don’t have an 
illustrator. 
Subclassing Book with Comic using STI forces you to allow illustrator to be 
null at the database level (for books that aren’t comics), and pushes your 
data integrity up into the application layer, which is not ideal. 
19 / 22
Reasons why you shouldn't use STI 
It prevents you from efficiently indexing your data 
Every index has to reference the type column, and you end up with 
indexes that are only relevant for a certain type. 
20 / 22
Reasons why you shouldn't use STI 
Two objects types have similar attributes 
Both airplanes and bicycles have wheels, but it probalby doesn’t make 
sense to group them into the same table, given that intuitively, they’re 
different objects that will have vastly different functionality and data 
fields in an application. 
Demo 
21 / 22
Thank you! 
22 / 22

Rupicon 2014 Single table inheritance

  • 1.
    Single Table Inheritance(STI) Raul Pristopan 1 / 22
  • 2.
    Agenda 1. Introduction 2. What is STI? 3. When should we use STI? 4. How do we implement STI? 5. Reasons why you shouldn't use STI 6. Demo 2 / 22
  • 3.
  • 4.
    Introduction In thebeginning everything always works. Things get complicated with time when you start adding new features. Some weeks ago I had to refactor some parts of a project and also add some new features to it. After some hours of reviewing the actual code I run into some problems: 1. Same code in multiple places 2. Same data stored in 2 or more tables create_table "users", :force => true do |t| ... t.string "image_file_name" t.string "image_content_type" t.integer "image_file_size" t.datetime "image_updated_at" ... 4 / 22
  • 5.
    Introduction create_table "activities",:force => true do |t| ... t.string "image_file_name" t.string "image_content_type" t.integer "image_file_size" t.datetime "image_updated_at" ... create_table "image_attachments", :force => true do |t| t.string "image_file_name" t.string "image_content_type" t.integer "image_file_size" t.datetime "image_updated_at" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.integer "used_by_count", :default => 1 t.text "original_url" end 5 / 22
  • 6.
    What is STI? STI is basically the idea of using a single table to reflect multiple models that inherit from a base model, which itself inherits from ActiveRecord::Base. In the database schema, sub-models are indicated by a single "type" column. In other words we have: a base class descendant classes 6 / 22
  • 7.
    When should weuse STI? Should be considered when dealing with model classes that share much of the same functionality and data fields (granular control over extending or adding to each class individually) When you have duplicate code over and over for multiple tables (and not being DRY) STI permits you to use keep your data in a single table while writing specialized functionality. 7 / 22
  • 8.
    When should weuse STI? Suppose you have three classes in your application which model similar things. To make this easier to think about I’ll be referring to some hypothetical classes by name: Business and Employee. Let’s consider three choices for modeling this situation: 1. Polymorphic Associations (separate classes, multiple tables) 2. Single Table Inheritance (separate classes, one table) 3. Single Class with conditionals (one class, one table) 8 / 22
  • 9.
    When should weuse STI? Polymorphic Associations (separate classes, multiple tables) With Polymorphic Associations we use modules to share code among classes. We have the following active record models for a “Business” and an “Employee”. class Business < ActiveRecord::Base # Methods, variables and constants end class Employee < ActiveRecord::Base # Methods, variables and constants end 9 / 22
  • 10.
    When should weuse STI? Polymorphic Associations (separate classes, multiple tables) For the contact information, we have the following active record models: class PhoneNumber < ActiveRecord::Base # Methods, variables and constants end class Website < ActiveRecord::Base # Methods, variables and constants end 10 / 22
  • 11.
    When should weuse STI? Polymorphic Associations (separate classes, multiple tables) Now, for the associations, Business and Employee models can have many phone numbers and websites. And conversely, Phone Number/Website can belong to either a Business model or an Employee model. class Business < ActiveRecord::Base has_many :phone_numbers, :as => phonable has_many :websites, :as => webable end class Employee < ActiveRecord::Base has_many :phone_numbers, :as => phonable has_many :websites, :as => webable end 11 / 22
  • 12.
    When should weuse STI? Polymorphic Associations (separate classes, multiple tables) class PhoneNumber < ActiveRecord::Base belongs_to :phonable, :polymorphic => true end class Website < ActiveRecord::Base belongs_to :webable, :polymorphic => true end Having the above setup, you can get a collection of phone numbers for a business instance via the call “@business.phone_numbers. Similarly, you can get a collection of phone numbers for an employee instance via “@employee.phone_numbers”. 12 / 22
  • 13.
    When should weuse STI? Single Class with conditionals (one class, one table) A Single Class is not exactly a design pattern, or anything particularly interesting. I’m thinking of a model with a type-like attribute (maybe called kind) and some if statements in methods where you need different behavior for different kinds of objects. 13 / 22
  • 14.
    When should weuse STI? Questions to ask yourself When deciding how to design your data models, here are some questions to ask yourself: 1. Are the objects, conceptually, children of a single parent? 2. Do you need to do database queries on all objects together? 3. Do the objects have similar data but different behavior? 14 / 22
  • 15.
    How do weimplement STI? Create the base class Model: Employee Attributes: Name - String Age - Integer Department - String $ rails generate model Employee name:string age:integer department:string # /app/models/employee.rb class Employee < ActiveRecord::Base # Methods, variables and constants end 15 / 22
  • 16.
    How do weimplement STI? Add a :type attribute as a string to the base class $ rails generate migration add_type_to_employee type:string 16 / 22
  • 17.
    How do weimplement STI? Create any necessary descendant classes # /app/models/developer.rb class Developer < Employee # Methods, variables and constants end # /app/models/tester.rb class Tester < Employee # Methods, variables and constants end 17 / 22
  • 18.
    Reasons why youshouldn't use STI It creates a cluttered data model Why don’t we just have one table called objects and store everything as STI? STI tables have a tendency to grow and expand as an application develops, and become intimidating and unweildy as it isn’t clear which columns belong to which models. 18 / 22
  • 19.
    Reasons why youshouldn't use STI It forces you to use nullable columns If sub-classes that you intend to use for STI have many different data fields, then including them all in the same table would result in a lot of null values and make it difficult to scale over time. In this case, you may end up with so much code in your model sub-classes that the shared functionality between sub-classes is minimal and warrants separate tables. A comic book must have an illustrator, but regular books don’t have an illustrator. Subclassing Book with Comic using STI forces you to allow illustrator to be null at the database level (for books that aren’t comics), and pushes your data integrity up into the application layer, which is not ideal. 19 / 22
  • 20.
    Reasons why youshouldn't use STI It prevents you from efficiently indexing your data Every index has to reference the type column, and you end up with indexes that are only relevant for a certain type. 20 / 22
  • 21.
    Reasons why youshouldn't use STI Two objects types have similar attributes Both airplanes and bicycles have wheels, but it probalby doesn’t make sense to group them into the same table, given that intuitively, they’re different objects that will have vastly different functionality and data fields in an application. Demo 21 / 22
  • 22.