Use Single Table Inheritance to Create an Admin User
Walk through how to integrate the advanced Rails tool of Single Table Inheritance (STI) to extend the functionality of the User class and follow best practices.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

In this guide, we are going to create an admin user using a tool called single table inheritance (STI).

Let's now talk a little about STI before we use it to create an admin user. If you open our schema.rb file, you'll see a table called users with a number of attributes. Now, if we want to create an admin user, probably the naive way would be to create another table and have the same attributes, like this:

large

However, this is a bad programming practice because we are duplicating code. Also, it can cause problems when we create our tests. To avoid this duplication, we are going to use object oriented inheritance to allow admin_users to share the same table as that of users.

To do that, go to your models folder and create a new file called admin_user.rb. If you open your user model file, user.rb, you'll see that this class inherits from ActiveRecord::Base. In fact, all model classes will inherit only from ActiveRecord::Base. But, our AdminUser will inherit from the User class. With this code, what we are doing is creating an admin user from the existing User class.

Now, I'm going to open my rails console, but in sandbox mode.

rails c --sandbox

The records that we create in this mode will be pulled out from the database and deleted at the end of the session, so it won't affect our development database. In this sense, this mode is more like a testing environment where we can safely try different scripts.

Let's now create a record for User table like this:

User.create!(email: "test@test.com", password: "asdfasdf", password_confirmation: "asdfasdf")

Next, we can create an AdminUser by running the following script:

Admin User.create!(email: "admintest@test.com", password: "asdfasdf", password_confirmation: "asdfasdf")

large

And this created an admin record for us. What's interesting about this code is that we don't have an AdminUser table in our schema file, and yet this record has been created for us.

To test, you can type:

User.last

And this will show the AdminUser object as this is the last record we created. Instead of User.last, we can also type AdminUser.last and it will bring up the same result. So, how did this happen?

The magic attribute that made this execution possible is the type attribute in our users table. This attribute controls the entire Single Table Inheritance component. When an object is created, the type attribute checks for its inheritance. When a User record was created, it goes to the User class and sees that it inherits from ActiveRecord::Base, so nothing is recorded. However, when an AdminUser is created, it goes to the AdminUser class and sees that it inherits from User. So, it sets the type for that record as AdminUser.

You can test this by storing the last record in a variable and printing its type.

u = AdminUser.last
u.type

This will display the value AdminUser. On the other hand, try printing the type attribute of the first record.

User.first.type

And the value will be nil because nothing was set.

large

Thus, we have two types of users sharing the same table. This functionality can be immensely useful when we are creating our admin dashboard in later guides. Also, this is considered to be the best way to implement sub-types of an object. Say you're creating a newspaper application where you have many types of users such as editors, readers and owners. Instead of creating different tables for each type, you can use single table inheritance for creating these records.

In the next video, we'll implement RSpec validations and test processes to help manage users.

Resources