1

I have a user model that I want to assign to be teacher or student( teacher and student are two separated model ) because if the user signup he would have different registration fields depending on if he is teacher or student. User can be a teacher or student, not both.

I have tried, but don't think that this is the best way to do it. Any help?

class User < AR
  has_secure_password

  has_one :teacher, class_name: "teacher", foreign_key: "teacher_id", conditions: { role: 'teacher' }
  has_one :student, class_name: "student", foreign_key: "student_id", conditions: { role: 'student' }

  enum role: [:teacher, :student]
end

class Teacher < AR
  belongs_to :user, class_name: "user", foreign_key: "user_id"
end

class Student < AR
  belongs_to :user, class_name: "user", foreign_key: "user_id"
end
ray
  • 5,454
  • 1
  • 18
  • 40
kubet
  • 55
  • 4
  • 1
    Use full-form for `AR` in your code snippet. Mention rails version as it smells like old version by optional parameter `conditions` provided in association method. Also you do not need to provide `class_name` & `foreign_key` as rails consider them by conventions. – ray Apr 29 '19 at 12:10
  • ApplicationRecord, 5.2.0 rails I know that conditions are deprecated, but what else to use? – kubet Apr 29 '19 at 12:14
  • Check this, https://stackoverflow.com/q/2462203/10522579 – ray Apr 29 '19 at 12:24
  • 3
    Using Single Table Inheritance must be a good option in your case. – Dyaniyal Wilson Apr 29 '19 at 13:57
  • Using the Single Tabel Inheritance you will get 3 models: User, Teacher, and Student, but it will be all 1 table and you can differentiate the type of the user through the form ... – Zalom Apr 29 '19 at 14:31

1 Answers1

1

This is how you can implement the STI for your case

class User < AR
  has_secure_password

  # Make all forms with User data send params with ':user' as a param key
  # instead of ':user_teacher'/':user_student'
  def self.model_name
    ActiveModel::Name.new(self, nil, 'User')
  end
end

class Teacher < User
  # custom methods 
end

class Student < User
  # custom methods
end

This way, you can have your form with form_for @user do # ....

One caveat, it is all placed in the Single table in the DB (hence the name Single Table Inheritance), and that means a lot of NULL values for the unrelated fields (say Teacher has a teacher_identification_number, and a user has student_identification_number which are different in size or they require different validation) for all the Students that attribute teacher_identification_number would be NULL, and vice-versa.

If the fields are much different between the two models, then you can analyze your data and put it in the different table to which only the Teacher/Student would have access to, that is called a Database Normalization (say Teacher has_many ClassInfo's or has_one JobInfo; or Teacher has_one TeacherProfile, and Student has_one StudentProfile or whatever).

It all really depends on how you model your DB.

References:
- Blog Post - Medium - STI
- Video Link - Drifting Ruby - STI
- Video Link - @ RailsCasts - STI
- Blog Post - StudyTonight - DB Normalization

Zalom
  • 696
  • 9
  • 18