Implement STI (single-table inheritance) in your Rails Models

November 29, 2010

  • Rails
  • Ruby
  • ruby-on-rails
  • inheritance
  • single-table-inheritance
  • sti

According to Wikipedia STI is

Single table inheritance is a way to emulate object-oriented inheritance in a relational database. When mapping from a database table to an object in an object-oriented language, a field in the database identifies what class in the hierarchy the object belongs to. In Ruby on Rails the field in the table called ‘type’ identifies the name of the class.

I hope that explains you what STI is, so lets see how can we implement this in our Rails Application. Let’s consider a scenario where we have a user, the user can be multiple types depending on the access rights, like Owner, Admin, Guest.

First Step : Create a class with the name of a specific type(For instance : Owner)

Create a new Ruby class file in your rails app/models directory and give it the name of one of the type/s e.g. Owner.rb

Create new Class

And similarly create classes for Admin and Guest

Second Step : Let these classes extend the User class

class Owner < User
end

Third Step : Add a type field to the User Model

This type field will store the type of user. ie. Owner, Admin or Guest. You can add the field as follows:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :name
      t.string :email
      t.string :password
      t.string :type
    end

    def self.down
      drop_table :user
    end
end

Fourth Step : Add the following code in the User Model

class << self
  def new_with_cast(*a,&b)
    if (h = a.first).is_a? Hash and (type = h[:type] || h['type']) and (klass = type.constantize) != self
      raise 'Error!!' unless klass < self # klass should be a descendant of User
      return klass.new(h, &b)
    end
    new_without_cast(*a, &b)
  end
  alias_method_chain :new, :cast
end

What we do here basically is, we are checking whether we are getting some value in the type field from the user submitted values. If we are getting some value we constantize the string and then we check whether the recieved constant is one of the descendants of the User class. If not then we raise an Exception. If the constant is one of the descendant of User class i.e. Owner, Admin, Guest, then the User becomes of that type.

Fifth Step : Changes in new.html.erb and edit.html.erb

In new and edit page of User add a select field which will determine the type of User

<p>
  <%= f.label :type %><br />
  <%= f.select :type, ['Owner', 'Admin', 'Guest'] %>
</p>

Last Step : We have done it. Time to test. That is it. We have successfully implemented the functionality of Single Table Inheritance. Lets take a look how things go.

User.create(:name => 'Rohit', :email => 'rohit0981989@gmail.com', :password => 'test123456' ,:type => 'Owner')
#This line will create a record in User model with type Owner.
@user = User.find_by_name('Rohit')
#This will give an object of type Owner, because we have set the type as Owner.

Everything seems to be working fine.

Thanks for reading the post. Any suggestions are most welcome.


LOCATION

Mumbai, Maharashtra, India

AROUND THE WEB

Copyright © 2021