0

I have two models, Clinician and Patient. A clinician has_many: patients and a patient belongs_to :clinician. A third model, SharedPatient is meant to store additional assosiactions between patients and clinicians as a patient can be shared by many other clinicians besides the one it belongs_to. This is done using a has_and_belongs_to_many relationship.

See models:

class Clinician < ActiveRecord::Base
 has_many :patients
 has_and_belongs_to_many :shared_patients, join_table: 'shared_patients'
end

class Patient < ActiveRecord::Base
 belongs_to :clinician
 has_and_belongs_to_many :shared_clinicians, join_table: 'shared_patients'
end

class SharedPatient < ActiveRecord::Base
 belongs_to :clinician
 belongs_to :patient
end

This is how my models are set out in the db:

Clinician:
 first_name: string
 last_name: string
 user_id: integer

Patient:
 clinician_id: integer
 first_name: string
 last_name: string
 user_id: integer

SharedPatient:
 clinician_id: integer
 patient_id: integer

Using these I would like to show the list of clinicians that a patient is shared with.

This is what I have in my controller now:

@patient = Patient.find_by(user_id: current_user.patient.id)
@clinicianslist = @patient.shared_clinicians

It am trying to show these in a view using:

<% @clinicianslist.each do |list| %>
 <p><%= link_to Clinician.find_by(id: list).full_name, clinician_path(list) %></p>
<% end %>

Using what I have now I am getting an error when trying to load the view:

NameError in Patients#show

uninitialized constant Patient::SharedClinician

I get the same error when running

Patient.find_by(id: 1259).shared_clinicians

in console.

Any advice on solving this error or on structuring the models to get the associations I want would be great. Thanks

Community
  • 1
  • 1
Skiapex
  • 153
  • 3
  • 14

2 Answers2

3

You don't need the SharedPatient model, so this should be deleted.

The error is because Rails cannot guess the class name from the association name or the table name. Try this:

class Clinician < ActiveRecord::Base
 has_many :patients
 has_and_belongs_to_many :shared_patients, join_table: 'shared_patients', class_name: 'Patient'
end

class Patient < ActiveRecord::Base
 belongs_to :clinician
 has_and_belongs_to_many :shared_clinicians, join_table: 'shared_patients', class_name: 'Clinician'
end

This tells Rails which class to use. Currently, it is guessing SharedClinician, which is wrong.

Matt Gibson
  • 14,616
  • 7
  • 47
  • 79
  • Yes.. it is true.. I was wrong. I just completed my reading of the doco.. :) +1 – Arup Rakshit May 26 '15 at 15:27
  • to seed the join table and the associations would the best way be to do this: `p = Patient.create(clinician_id: clinician_a.id, first_name: "John", last_name: "Doe", user_id: userp.id ) p.clinicians << Clinician.find_by_id("300")` – Skiapex May 26 '15 at 18:23
  • or with `shared_clinicians` instead of `clinicians` – Skiapex May 26 '15 at 18:29
  • @Matt, if I set it up as you describe and input `Patient.find_by(id: 1307).shared_clinicians` I get `Patient Load (0.9ms) SELECT "patients".* FROM "patients" WHERE "patients"."id" = 1307 LIMIT 1 Clinician Load (0.5ms) SELECT "clinicians".* FROM "clinicians" INNER JOIN "shared_patients" ON "clinicians"."id" = "shared_patients"."clinician_id" WHERE "shared_patients"."patient_id" = $1 [["patient_id", 1307]] PG::UndefinedTable: ERROR: relation "shared_patients" does not exist at character 52` – Skiapex May 26 '15 at 21:03
  • Have you made a db migration to create the table? – Matt Gibson May 27 '15 at 08:35
0

I would say that in this case it does not really make that much sense with a join model. It would make sense if it for example was a Appointment object with its own data and logic.

Instead you should create a patients_clinicians join table, and define the relation as:

class Clinician < ActiveRecord::Base
  has_and_belongs_to_many :patients
end

class Patient < ActiveRecord::Base
  has_and_belongs_to_many :clinicians
end

Convention over configuration...

max
  • 96,212
  • 14
  • 104
  • 165
  • You actually don't need to create a new join table, you could simply rename `shared_patients`. – max May 26 '15 at 15:29
  • If I did this I wouldn't have a primary `clinician` attached to the `patient` through `Patient` having `clinician_id`. I would need to have this relationship created separately as the patient is created - can I do `accepts_nested_attributes_for` on join tables? – Skiapex May 26 '15 at 16:11
  • Yes you can use nested attributes. – max May 26 '15 at 16:15
  • No, using HABM would not effect the "primary clinician" relation. I just omitted that part for brevity. – max May 26 '15 at 16:18
  • do you think this is a better option than a has_many :through association like `:has_many :clinicians, :through => :sharedrelationship`? – Skiapex May 26 '15 at 20:53
  • Its not better or worse, but sometimes it just does not make sense with join models. – max May 26 '15 at 22:45