Search code examples
ruby-on-railsruby-on-rails-3ruby-on-rails-4ruby-on-rails-3.2ruby-on-rails-3.1

Rails - adding new field to inherting class


I have Student model, inheriting User model

class Student < User

if I add new field to Student, its not showing up. All I see is replica of User fields in Student table.

rails g model User email:string name:string gender:boolean
rails g model Student age:integer

rake db:migrate

User model :

class User < ActiveRecord::Base
validates :email, :name, presence: true
end

then I replaced
class Student < ActiveRecord::Base with

class Student < User
end

now the :age field is replaced by :email, :name, :gender fields in Student table, I don't have access to :age field anymore

Student should have User fields as well as additional fields of its own.
how do I achieve this?


Solution

  • I think you're getting confused between tables and models in Rails.

    As mentioned in the comments, you have a Single Table Inheritance setup; you'll have a single users table which can be extrapolated to different classes (models) using the Type attribute:

    #app/models/user.rb
    class User < ActiveRecord::Base
       #columns id | type | other | user | attributes | created_at | updated_at
    end
    
    #app/models/student.rb
    class Student < User
       # uses "users" table
    
       def custom_method
         #=> Will only appear with @student.custom_method (IE @user.custom_method will not exist)
       end
    end
    

    This means that you don't have two tables in this instance; Student will be using the User table.

    If you wish to use custom attributes in the Student model, you're able to (outlined above). Ultimately, with STI, you have to use the same table for all the inherited models. If you need to add extra attributes, you're going to have to append to the "parent" table.

    --

    Student should have User fields as well as additional fields of its own

    If there are a lot of attributes, you'll have to set up another table to store them, and then associate the two models. It will be messier, but it's better than storing huge amounts of empty cells in a single table:

    #app/models/user.rb
    class Student < ActiveRecord::Base
       has_one :profile
    end
    
    #app/models/profile.rb
    class Profile < ActiveRecord::Base
       belongs_to :student
    end
    

    This is how we store our users in some of our apps:

    enter image description here

    This gives us the ability to call @user.profile.homepage etc, or if we wanted to delegate it:

    #app/models/user.rb
    class User < ActiveRecord::Base
       has_one :profile
       delegate :homepage, to: :profile, prefix: true #-> @user.profile_homepage
    end