Reputation: 461
How to implement inheritance with active records?
For example, I want a class Animal, class Dog, and class Cat.
How would the model and the database table mapping be?
Upvotes: 40
Views: 30604
Reputation: 35595
One particular way of doing this is via Delegated Types - this makes sense only if you want to paginate all animals together, and to view cats and dogs together, then the delegated type is particularly useful. I also like it because you don't need to have empty columns, for where it doesn't make sense, as is the case with Single Table Inheritance solutions.
# Schema: entries[ id, created_at, updated_at, animalable_type, animalable_id ]
class Animal < ApplicationRecord
delegated_type :animalable, types: %w[ Cat Dog ]
end
module Animalable
extend ActiveSupport::Concern
included do
has_one :animal, as: :animalable, touch: true
end
end
# Schema: cats[ id, selfishness_level ]
class Cat < ApplicationRecord
include Animalable
end
# Schema: dogs[ id, favourite_game, wag_tail_level ]
class Dog < ApplicationRecord
include Animalable
end
Upvotes: 1
Reputation: 7879
ActiveRecord supports mapping inheritance hierarchies to a single table(Single-table inheritance. Table would have a column type
which stores name of actual class and is used to select other class-specific columns.
It is possible to implement multi-table inheritance mapping, as shown here, but this particular way is not portable, AFAIK.
Upvotes: 7
Reputation: 12001
Models:
class Animal < ActiveRecord::Base; end
class Dog < Animal; end
class Cat < Animal; end
Migration:
class CreateAnimals < ActiveRecord::Migration
def self.up
create_table :animals do |t|
# Other attributes...
t.string :type
end
end
def self.down
drop_table :animals
end
end
Upvotes: 17
Reputation: 37133
Rails supports Single Table Inheritance.
From the AR docs:
Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed by overwriting Base.inheritance_column). This means that an inheritance looking like this:
class Company < ActiveRecord::Base; end class Firm < Company; end class Client < Company; end class PriorityClient < Client; end
When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = "Firm". You can then fetch this row again using Company.find(:first, "name = ‘37signals’") and it will return a Firm object.
If you don‘t have a type column defined in your table, single-table inheritance won‘t be triggered. In that case, it‘ll work just like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
A pretty good tutorial is here: http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
Upvotes: 67