Skip to content

Single Table Inheritance

Updated: at 02:12 AM (4 min read)

Single Table Inheritance (STI) is a powerful feature in Ruby on Rails that allows multiple models to share the same database table, reducing redundancy and improving code organization. In this article, we’ll explore the concept of STI, walk through a practical example, and discuss when and why you should use it.

Table of contents

Open Table of contents

Introduction

In Rails, Single Table Inheritance (STI) lets multiple models inherit from a parent class while sharing the same database table. This is achieved by adding a type column to the table, which tells ActiveRecord which subclass a particular record belongs to. STI helps avoid creating multiple tables for similar models, simplifying your database schema and improving performance.

STI Example

Imagine we have a Company model that manages different types of cars, and we want to categorize these cars into sports cars and luxury cars, all while storing them in the same table. STI makes this possible without the need for multiple tables, allowing us to distinguish between car types using the type column.

class Company < ApplicationRecord
  has_many :cars
end

Create the Car model

Next, we define a parent Car model:

class Car < ApplicationRecord
	belongs_to :company
end

This sets up the basic relationship between companies and cars. However, we still need a way to specify different car types.

Create subclasses of the Car model

We can now create subclasses for different types of cars, like sports cars and luxury cars, which will inherit from the Car model:

# sport_car.rb
class SportsCar < Car
end

# luxury_car.rb
class LuxuryCar < Car
end

At this point, both SportsCar and LuxuryCar models are tied to the cars table, and the type column will help differentiate between the two.

Update the Company model relationships

class Company < ApplicationRecord
	has_many :cars

	has_many :sports_cars
	has_many :luxury_cars
end

This structure allows us to easily retrieve all cars of a specific type that belong to a company.

Schema

After setting up STI, here’s what the database schema will look like:

ActiveRecord::Schema[7,0].define(version: some_version_here) do
	create_table "cars", force: :cascade do |t|
		t.string "type" # The 'type' column distinguishes the subclass (e.g., SportsCar, LuxuryCar). You can add as many as you need!
		t.string "name"
		t.string "model"
		t.integer "price"
		t.integer "company_id", null: false
		t.datetime "created_at", null: false
		t.datetime "updated_at", null: false
		t.index ["company_id"], name: "index_cars_on_company_id"
	end

	create_table "companies", force: :cascade do |t|
		t.string "name"
		t.string "location"
		t.datetime "created_at", null: false
		t.datetime "updated_at", null: false
	end

	add_foreign_key "cars", "companies"
end

Notice the type column in the cars table—it’s key to implementing STI, as it tells Rails which subclass a particular record belongs to.

Testing in Rails Console

To see this in action, let’s jump into the Rails console. Follow these steps:

chevrolet = Company.create(name: 'Chevrolet', location: 'Some location')

chevrolet.sports_cars.create(name: 'Corvette', model: 'STINGRAY 2LT COUPE', price: 80000)

chevrolet.sports_cars

This will return all records where the type is SportsCar.

chevrolet.luxury_cars

This will return an empty array if no luxury cars are associated with Chevrolet.

chevrolet.cars

This will return all car records for the company, both sports and luxury.

When to use STI?

STI is a great solution when:

However, if the models diverge too much in behavior or fields, STI might not be the best fit.

Benefits of STI

Conclusion

Single Table Inheritance (STI) is a powerful tool in Rails that simplifies your schema and helps you write DRY, maintainable code. It’s a great fit when you have models that share the same attributes and can be easily differentiated by type. As with any pattern, use it when it makes sense for your application’s data structure.

Thank you for reading, and happy coding!