40

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?

1
  • 5
    you might want to accept an answer Commented May 17, 2013 at 4:27

4 Answers 4

67

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/

Sign up to request clarification or add additional context in comments.

Comments

17

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

Comments

7

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.

1 Comment

This is now possible with rails out of the box, using delegated types
2

Delegated Types

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.