ActiveRecord support for PostgreSQL's native inherited tables (multi-table inheritance).
- Ruby 2.7+
- ActiveRecord 5.2+ (tested through Rails 8.1)
- PostgreSQL 9.4+
Add to your Gemfile:
gem 'active_record-mti'PostgreSQL supports table inheritance via CREATE TABLE child () INHERITS (parent).
Child tables inherit all columns from the parent, can add their own, and rows in
child tables appear in queries against the parent. Each row carries a tableoid
system column identifying which physical table it belongs to.
ActiveRecord::MTI uses tableoid to automatically instantiate the correct
Ruby class when querying through a parent table — no discriminator column needed.
class Account < ActiveRecord::Base
# table: accounts
end
class User < Account
# table: account/users (auto-inferred)
end
class Admin < User
self.table_name = 'account/admins'
endIn most cases, you don't need to do anything beyond installing the gem. MTI will detect when a model maps to a PostgreSQL inherited table and handle instantiation automatically.
Child table names follow singular_parent/plural_child:
| Model | Inferred Table Name |
|---|---|
Account |
accounts |
User |
account/users |
Developer |
account/developers |
Hacker |
account/developer/hackers |
Override with self.table_name = '...' when needed.
# Queries all child tables via the parent — returns mixed types
Account.all
# => [#<User ...>, #<Admin ...>, #<Developer ...>]
# Queries only the child table
Admin.where(active: true)
# => [#<Admin ...>]
# Associations work transparently
post.commenters # may return User, Admin, Developer instancesThe default SELECT includes tableoid for subclass discrimination:
SELECT "accounts".*, "accounts"."tableoid" FROM "accounts"class CreateAccounts < ActiveRecord::Migration[7.1]
def change
create_table :accounts do |t|
t.jsonb :settings
t.timestamps
end
create_table 'account/users', inherits: :accounts do |t|
t.string :email
end
create_table 'account/admins', inherits: 'account/users' do |t|
t.integer :access_level
end
end
endChild tables automatically inherit the parent's primary key and indexes.
rake db:schema:dump produces a schema that preserves the inheritance chain:
create_table "accounts", force: :cascade do |t|
t.jsonb "settings"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "account/users", inherits: "accounts" do |t|
t.string "email"
endA model can use both. The child table can have a type column for STI within
the MTI branch:
class Vehicle < ActiveRecord::Base
# table: vehicles (has a `type` column)
end
class Truck < Vehicle
# table: vehicle/trucks (MTI — own table, inherits from vehicles)
end
class Pickup < Truck
# STI — shares vehicle/trucks table, discriminated by `type`
end- Child-specific columns are not available when querying through the parent
table.
Message.allreturns the correct subclass, but only parent columns are populated. Call.reloador query the child class directly to access child-specific attributes.
- Fork it
- Create your feature branch (
git checkout -b my-feature) - Commit your changes
- Push to the branch
- Create a Pull Request