ActiveRecord support for PostgreSQL's native inherited tables (multi-table inheritance)
Supports
- Ruby:
2.3
,2.4
,2.5
- ActiveRecord:
4.2
,5.0
,5.1
Confirmed production use in 4.2
Add this line to your application's Gemfile:
gem 'active_record-mti'
And then execute:
$ bundle
Or install it yourself as:
$ gem install active_record-mti
class Account < ::ActiveRecord::Base
# table_name is 'accounts'
# ...
end
class User < Account
# table_name is 'account/users'
# ...
end
class Developer < Account
# table_name is 'account/developers'
# ...
end
class Admin < User
self.table_name = 'admins'
# ...
end
class Hacker < Developer
# table_name is 'account/developer/hackers'
# ...
end
In most cases, you shouldn't have to do anything beyond installing the gem. ActiveRecord::MTI
will do it's best to determine the nature of inheritance in your models. If your models map to their own tables, ActiveRecord::MTI
will step in and make sure inheritance is treated appropriately. Otherwise it will gracefully acquiesce to ActiveRecord
's built-in STI
. (see Table Names section below).
ActiveRecord
queries work as usual with the following differences:
- The default query of "*" is changed to include the OID of each row for subclass discrimination. The default select will be
SELECT "accounts"."tableoid" AS tableoid, "accounts".*
(for example)
Conventionally—to indicate the nature of inheritance—ActiveRecord::MTI
expects the table_name
of a child model to follow the singular_parent_table_name/plural_child_table_name
pattern. As always, if you need to deviate from this, you can explicitly set the table_name
as shown below, or configure ActiveRecord::MTI
using the configure block.
Note, ActiveRecord::MTI
will fall back on the unnested table_name
if it's unable to find the nested form, and short of that, it will use the superclass's table_name
.
ActiveRecord::MTI
can be configured using a configure block.
# config/initializers/active_record_mti.rb
ActiveRecord::MTI.configure do |config|
config.table_name_nesting = true
config.nesting_seperator = '/'
config.singular_parent = true
end
In your migrations define a table to inherit from another table:
class CreateAccounts < ActiveRecord::Migration
def change
create_table :accounts do |t|
t.jsonb :settings
t.timestamps null: false
end
create_table :users, inherits: :accounts do |t|
t.string :firstname
t.string :lastname
end
create_table :developers, inherits: :users do |t|
t.string :url
t.string :api_key
end
end
end
A schema will be created that reflects the inheritance chain so that rake:db:schema:load
will work
ActiveRecord::Schema.define(version: 20160910024954) do
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 "users", inherits: "accounts" do |t|
t.string "firstname"
t.string "lastname"
end
create_table "developers", inherits: "users" do |t|
t.string "url"
t.string "api_key"
end
end
- Fork it ( https://github.com/TwilightCoders/active_record-mti/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request