Rails Single Table Inheritance
Ruby on Rails supports Single Table Inheritance. Single Table Inheritance allows you to have one single SQL table, such as an employees table, manage different type of employees like managers and developers. A manager would normally have more responsibilities than a developer and you can separate these additional responsibilities in a different ActiveRecord model class.
Here is another example of Single Table Inheritance that you might find useful. You might have to support a hierarchy of users for you web application, such as User, Editor, and Administrator. An Administrator might have full Create, Read, Update, and Delete (CRUD) access while a User might just have read access. For this hierarchy of users you will need to create a Ruby class for each type and place it in the Rails app/models directory. Here is the base User class:
class User < ActiveRecord::Base end
My Editor class:
class Editor < User end
And the Administrator class:
class Administrator < Editor end
Place each of these classes in their own Rails model file under the app/models directory.
To indicate to Ruby on Rails that the users table needs to support Single Table Inheritance you need to add a column named ‘type’ to the users table. Here is my users table definition:
CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT, user VARCHAR(15) NOT NULL UNIQUE, pass VARCHAR(40) NOT NULL, type VARCHAR(20) NOT NULL, PRIMARY KEY (id) );
In the column named type you should store the name of the class, the class type, that should be used for each user. To mark an certain user as an admin set his type to ‘Administrator’. By setting a user’s type to ‘Administrator’ you are giving him full administrator privileges as defined in your Administrator model class.
And now, in your controller you can look up all users that are administrators by using the following bit of code:
allAdmins = Administrator.find(:all)
If you want to look up all known users you can do so by using the User class as in the following snippet of code:
allUsers = User.find(:all);
At this point, you would add additional responsibilities to editors and administrators by adding methods to those model classes.
Related posts:
April 1st, 2007 at 10:48 pm
A quick question: would you still have controllers for Administrator, Editor and User, or just a single one for User? This stuff is confusing. Is it possible to have an ActiveRecord model inherit from another ActiveRecord model – so as an example you extend the original User model with additional fields in a Editor model?
On a side note:
Your db table definition will restrict you user.type to 10 characters, so your example of specifying ‘Administrator’ will fail.
April 2nd, 2007 at 3:35 pm
@Kapslok – Yes, an Administrator model can extend the User model. The name of the database table would be users. The users table would store both administrator and regular end users.
In general, a model can extend from any other model or from ActiveRecord directly. The name of the database table would be the plural of the class that extends ActiveRecord directly.
A model does not have to be paired with a controller so you don’t have to have a controller for each model.
Thanks, for letting me know about the the column type being 10 character wide. I originally named the administrator class Admin.
August 10th, 2007 at 6:50 am
Thanks for this, I’ve been doing some rather confusing Single Table Inheritance stuff for the first time recently, and this has been (and will continue to be) a very good resource.
December 14th, 2007 at 10:24 am
Crisp, clear and to the point. Ever thought of writing for the RoR Wiki? ;)
July 24th, 2008 at 9:00 am
[...] 6. OO Inheritance (RoR supports Single Table Inheritance) – Rails Single Table Inheritance[...]
November 27th, 2008 at 1:35 am
thank’s for the explain
i just realize STI in ruby on rails is very simple, and the part i like is, it don’t need to create additional table, just create additional model
March 23rd, 2009 at 12:45 pm
It looks like to_xml does weird stuff with STI. Consider
user = User.find(3)
puts user.class
This gives Administrator as above. However, then do
user.to_xml
It comes back looking like:
Barb
…
I want to have the outer XML wrapper still be and have administrator inside the XML as a or something. Any way to do that without writing a substitute to_xml function?
TIA
March 23rd, 2009 at 11:03 pm
Hi. Thanks for the post.
One point: Shouldn’t your Administrator model inherit from User? It is inheriting from Editor in your example.
March 26th, 2009 at 4:24 am
The Administrator model can inherit from either Editor or User. If it inherits from Editor, it has all of the privileges of Editor, so it makes more sense to inherit from it.
June 7th, 2009 at 4:06 pm
Thanks for the information. Can i create a separate table for Administrator and Editor if i want to store custom attributes. I mean the attributes for Administrator and Editor which are not generic and cannot be fitted into the User model.
June 13th, 2009 at 3:33 pm
I have some question, I’d thank you guys if you can help me, I have the same question than Pratik: how do you handle custom attributes for each role? I wouldn’t like to have one table per role. another question is: could the type column be replaced by a dynamic attribute in the model? thanks in advance
July 21st, 2009 at 3:04 am
Can we put all the inherited models in the same file along with the parent class? When I try to do model.singularize.classify.constantize, I get a “Uninitialized contant model”. Weird thing is, it works okay on the console, but not when I run the app. Thanks
August 14th, 2009 at 1:56 pm
[...] to get the most out of your table relationships at guides.rubyonrails.org. And I ran across a post by Techknow that does a great job of describing Single Table [...]