Search code examples
ruby-on-railsmigrationassociationshas-many-throughrails-console

Rails association has_many through not working with multiple models


I am a newbie to rails. I wanted to try out has_many through association with the following example.

  1. There are two types of doctors. operation_doctors and treatment_doctors.
  2. There are two types of patients. operation_patients and treatment_patients.

Relation: Operation_doctors can only make an appointment with operation patients and vice versa. Same applies for treatment doctors.

Doctor.rb

class Doctor < ActiveRecord::Base
has_many :appointments
has_many :operation_patients, :class_name => 'Patient', :source => :operation_patient ,through: :appointments
has_many :treatment_patients, :class_name => 'Patient', :source => :treatment_patient ,through: :appointments
end

Patient.rb

class Patient < ActiveRecord::Base
has_many :appointments
has_many :operation_doctors,:class_name => 'Doctor', :source => :operation_doctor,through: :appointments
has_many :treatment_doctors,:class_name => 'Doctor', :source => :treatment_doctor,through: :appointments
end

Appointment.rb

class Appointment < ActiveRecord::Base
belongs_to :operation_doctor, :class_name => 'Doctor', :foreign_key => :operation_doctor
belongs_to :treatment_doctor, :class_name => 'Doctor', :foreign_key => :treatment_doctor
belongs_to :operation_patient, :class_name => 'Patient', :foreign_key => :operation_patient
belongs_to :treatment_patient, :class_name => 'Patient', :foreign_key => :treatment_patient
end

Error in Rails console: Input:

@op1 = Patient.new
@od1 = Doctor.new 

Error:

irb(main):023:0> @op1.operation_doctors << @od1
(0.1ms)  begin transaction
(0.1ms)  rollback transaction
ActiveRecord::UnknownAttributeError: unknown attribute 'patient_id' for Appointment.

Solution

  • In patient.rb

    class Patient < ActiveRecord::Base
      has_many :appointments
    

    For this to work, you need a patient_id field on the Appointment table. Your actual appointment relation is more complex. There are 2 ways for resolving the error.

    Two appointments on the patient model

    class Patient < ActiveRecord::Base
      has_many :operation_appointments, :class_name => 'Appointment', :foreign_key => :operation_patient
      has_many :treatment_patients, :class_name => 'Appointment', :foreign_key => :treatment_patient
    

    You will need to do the same thing for the doctor's model.

    Single Table Inheritance

    I would be inclined to make a larger datamodel for this solution. Since you know that doctors have two types and patients have two types, they are incompatible with each other, and they have logic that would be duplicate, I would lean on inheritance.

    Here's a sketch of my data model:

    ActiveRecord::Base
     Doctor               Patient                 Appointment
      OperationDoctor      OperationPatient        OperationAppointment
      TreatmentDoctor      TreatmentPatient        TreatmentAppointment
    

    The Appointment table would contain a doctor_id, patient_id, and type field

    rails g model appointment type:string patient:references doctor:references
    rails g model operation_appointment --parent appointment
    

    This would simplify the classes:

    # appointment.rb
    belongs_to :patient
    belongs_to :doctor
    
    # patient.rb
    has_many :appointments
    has_many :doctors, :through => :appointment
    

    This way your doctor would either be a TreatmentDoctor or a OperationDoctor, and you can use validation on those models to ensure the patient is of the correct type.