The Foundation

All the 6 basic associations that I covered in Part 1, Part 2, Polymorphic & Self-Join had one thing in common, all of them had at least 1 foreign key in any one table. However, foreign-key isn’t needed to implement STI because as the name suggests, Single Table Inheritance, requires Single Table.

A single table without a foreign key? How would we associate with anything without a foreign key? A better question, what is the purpose of an STI in the first place?

To answer the question let's discuss an example. Let's say we have a table A that has x number of attributes in it. And we wanted to create another table B with the same-to-same attributes as the table A. And then we wanted to create another table with the same attributes and so on… You see where this is going. The only difference between these tables is its name, the rest of it such as its attributes’ name and its type are the same for all tables. The concept of repeating ourselves with the same code is not what Ruby is about. STI helps us to avoid the creation of multiple tables with similar attributes with a single table. By using STI, we follow Do-not Repeat Yourself (DRY) concept.

STI is a Rails technique to categorize multiple types in a single table by having multiple types model classes that inherit from single tables’ model class.

An Example

Let's understand the implementation of STI with an example.

“A list of entertainment shows, such as a movie, TV-series or a documentary.”

Movies, TV series, and documentaries have a lot of common features such as name, runtime, release_date, language etc. So instead of creating 3 different tables of these, we create a single table that holds records of all.

But how would we know whether a particular record in a table is of a movie, TV series, or documentary? We explicitly create an attribute in the table named ‘type’ where each record can either have a value of as a movie, TV series, or documentary.

It is this STI attribute, type, that lets rails know that we are using STI association. It's a convention provided by Rails that we follow in order to enable a certain feature.

Note, a foreign-key name followed by _type is a polymorphic attribute used for polymorphic association.

Create Table

Now, let's create a table with the example discussed above. Since the table will hold the values of different types, we would appropriately name the table that categorizes our types.

rails g model Show name:string streaming_at:string released_on:date type:string

As discussed above, to implement STI we create type attribute.

Migration file:

The Show Model class file:

class Show < ApplicationRecord
end

Create Types

A shows table is a representation of all 3 types in our example, hence, we create only their model files without a table for each.

rails g model Movie --parent=Showrails g model TvSeries --parent=Showrails g model Documentary --parent=Show

The — parent option let rails know that we want to create model files without a table & a migration file and inherit model class directly from the Show class instead of the ApplicationRecord class. This means that all behavior added to Show class is available for Movie, TvSeries & Documentary model class too, such as associations, public methods, validations, etc.

A peek into the model files generated :

# app/model/tv_series.rb
class TvSeries < Show
end
# app/model/movie.rb
class Movie < Show
end
# app/model/documentary.rb
class Documentary < Show
end

Data

Now to save records in the Show table we would save it the same way as if we had 3 different tables for each type as:

# movie = Movie.create(name: "Moonlight", streaming_at: "Amazon Prime", released_on: "18/11/2016")# tv = TvSeries.create(name: "Tiny World", streaming_at: "Apple TV+", released_on: "02/10/2020")# doc = Documentary.create(name: "Life in Colour", streaming_at: "Netflix", released_on: "22/04/2021")
The data we created in 'shows' table.

Rails automatically set the value of the type for each record by the name of the model class we initiated while creating a record.

Extracting data

$ Movie.all
=> #<ActiveRecord::Relation [#<Movie id: 1, name: "Moonlight", streaming_at: "Amazon Prime", released_on: "2016-11-18", type: "Movie", created_at: "##", updated_at: "##">]
$ Documentary.all
=> #<ActiveRecord::Relation [#<Documentary id: 3, name: "Life in Colour", streaming_at: "Netflix", released_on: nil, type: "Documentary", created_at: "##", updated_at: "##">]
$ Show.all
=> #<ActiveRecord::Relation [#<Movie id: 1, name: "Moonlight", streaming_at: "Amazon Prime", released_on: "2016-11-18", type: "Movie", created_at: ##, updated_at: ##>, #<TvSeries id: 2, name: "Tiny World", streaming_at: "Apple TV+", released_on: "2020-10-02", type: "TvSeries", created_at: ##, updated_at: ##>, #<Documentary id: 3, name: "Life in Colour", streaming_at: "Netflix", released_on: "2021-04-22", type: "Documentary", created_at: ##, updated_at: ##>]

I hope I have achieved to make you understand the topic thoroughly. If you have any questions or other comments, feel free to leave them below. Also, if you found this article useful, you can share it so others can find it as well.

--

--

--

Jr. Ruby on Rails Dev | Math Enthusiast | Ex-Machine Learning Engineer | https://juzershakir.github.io/

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Cara Mengakses Sensor Salinitas Atmega CVAVR

Data Science on Mac M1: An eventful roller-coaster!

[Utilizing the Recommendation Center] On Reco Widget Performance & Promoted Items

Code Smell 99 — First Second

Code Smell 40 — DTOs

[LeetCode]#175. Combine Two Tables

Building a dirty search engine with Elasticsearch and web-crawler in Python

How To Publish Your First App On Google Play

Get the Medium app

Juzer Shakir

Juzer Shakir

Jr. Ruby on Rails Dev | Math Enthusiast | Ex-Machine Learning Engineer | https://juzershakir.github.io/

More from Medium

Introduction to Object Relationships in Basic Ruby

How to Create a Rails 6 API with Devise-JWT

How to Use the Destroy and Delete Methods in Rails

Build A Simple Search Feature In Rails