I am using Ruby on Rails 3 and I implemented a working Single Table Inheritance like the following:
class User < ActiveRecord::Base
# Schema Information
#
# Table name: User
#
# id : integer
# type : string
# children_user_id: integer
...
end
class UserAdmin < User
# Schema Information
#
# Table name: UserAdmin
#
# id : integer
# special_field1 : string
# special_field2 : string
# ...
...
end
class UserCommon < User
# Schema Information
#
# Table name: UserCommon
#
# id : integer
# another_field1 : string
# another_field2 : string
# ...
...
end
I would like to know if, on creating an UserAdmin
record (or an UserCommon
record) in the User
table running the following
UserAdmin.create(:children_user_id => "1")
# or UserCommon.create(:children_user_id => "1")
it is possible to "automatically" create in someway (possibly the "Rails Way"!) a new record also in the UserAdmin
table (or UserCommon
table) that has its own fields (at the database level, those fields are columns). I would like to do that in order to "better handle" UserAdmin
because this class has different and more attributes of the UserCommon
class.
If it is possible, how can I do that (maybe using association model statements, callbacks, polymorphism, ...)? Do you have some advice on this issue?
The thing with single table inheritance is it is based on a single table model, not surprisingly, so you can't have different classes using different tables.
Typically the "Rails Way" is to bundle together all the possible fields that are required into a single table and use STI to handle the data mapping and validation issues for you. There are limits on what it can hide from the application, though, as generally a field being defined means it's usable by any of the classes bound to that table. Most people do not consider this to be an issue.
What you probably want to do is make a record that's joined in depending on the user type, for instance:
class User < ActiveRecord::Base
end
class AdminUser < User
belongs_to :admin_profile
end
class CommonUser < User
belongs_to :common_profile
end
This would require the users
table having an admin_profile_id
and common_profile_id
column where the admin_profiles
and common_profiles
tables contain the required additional fields.
The attributes in these tables can be mapped back into the base class using the delegate
method as required.
Moving the extra fields into a separate table may help compartmentalize things, but it also means reads will be slower because of the required join and the possibility of inconsistent records, due to one part being missing or out of date, is increased.
Generally you are okay loading all of the user-related fields into a single table even if many of these fields are not frequently used. The storage cost of a NULL field is typically low in the scheme of things and unless there are hundreds of these fields, the additional complexity exposed to the developer is minimal, a smaller price to pay than to have to create and reference records in pairs constantly.