27

I have just jumped into has_many :through association. I'm trying to implement the ability to save data for all 3 tables (Physician, Patient and association table) through a single form.

My migrations:

class CreatePhysicians < ActiveRecord::Migration
  def self.up
    create_table :physicians do |t|
      t.string :name
      t.timestamps
    end
  end
end

class CreatePatients < ActiveRecord::Migration
  def self.up
    create_table :patients do |t|
      t.string :name
      t.timestamps
    end
  end
end

class CreateAppointments < ActiveRecord::Migration
  def self.up
    create_table :appointments do |t|
      t.integer :physician_id
      t.integer :patient_id
      t.date :appointment_date
      t.timestamps
    end
  end
end

My models:

class Patient < ActiveRecord::Base
  has_many :appointments
  has_many :physicians, :through => :appointments
  accepts_nested_attributes_for :appointments
  accepts_nested_attributes_for :physicians
end
class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, :through => :appointments
  accepts_nested_attributes_for :patients
  accepts_nested_attributes_for :appointments
end
class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end

My controller:

def new
    @patient = Patient.new
    @patient.physicians.build
    @patient.appointments.build
end

My view (new.html.rb):

<% form_for(@patient) do |patient_form| %>
  <%= patient_form.error_messages %>
  <p>
    <%= patient_form.label :name, "Patient Name" %>
    <%= patient_form.text_field :name %>
  </p>
  <%  patient_form.fields_for :physicians do |physician_form| %>
    <p>
      <%= physician_form.label :name, "Physician Name" %>
      <%= physician_form.text_field :name %>
    </p>
 <% end %>

  <p>
    <%= patient_form.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', patients_path %>

I'm able to create a new Patient, Physician and associated record for an Appointment, but now I want to have field for appointment_date too in form. Where should I place fields for Appointments and what changes are required in my controller? I tried googling and tried this, but got stuck in some or other error implementing it.

Community
  • 1
  • 1
Wanderer
  • 614
  • 2
  • 18
  • 41
  • 1
    Another tip: You could use `t.references :patient` instead of `t.integer :patient_id` to create your column of the appropriate type. See http://guides.rubyonrails.org/migrations.html#special-helpers – Robin Nov 22 '12 at 07:05

3 Answers3

26

Ok, this little bugger of a question stumped me for a few hours, so I'm going to post my working solution on here in hopes it shaves some time for peeps. This is for Rails 4.0 and Ruby 2.0. This also overcame a "symbol to integer conversion" issue I had.

Models:

class Patient < ActiveRecord::Base 
  has_many :appointments
  has_many :physicians, :through: :appointments
  accepts_nested_attributes_for :appointments
end 

class Appointment < ActiveRecord::Base
  belongs_to :physician 
  belongs_to :patient
  accepts_nested_attributes_for :physician
end

class Physicians < ActiveRecord::Base
  has_many :appointments
  has_many :patients, through: :appointments
end

Controller:

def new
  @patient= Patient.new 
  @appointments = @patient.appointments.build
  @physician = @appointments.build_physician 
end

def create
  Patient.new(patient_params)
end


def patient_params
   params.require(:patient).permit(:id, appointments_attributes: [:id, :appointment_time, physician_attributes: [:id ] )
end

View

<% form_for(@patient) do |patient_form| %>
  <%= patient_form.error_messages %>
  <p>
    <%= patient_form.label :name, "Patient Name" %>
    <%= patient_form.text_field :name %>
  </p>

  <% patient_form.fields_for :appointments do |appointment_form| %>
    <p>
      <%= appointment_form.label :appointment_date, "Appointment Date" %>
      <%= appointment_form.date_field :appointment_date %>
    </p>

    <% appointment_form.fields_for :physician do |physician_form| %>
      <p>
        <%= physician_form.label :name, "Physician Name" %>
        <%= physician_form.text_field :name %>
      </p>
    <% end %>
  <% end %>

  <p>
    <%= patient_form.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', patients_path %>
jvperrin
  • 3,322
  • 1
  • 21
  • 33
guy8214
  • 1,045
  • 2
  • 12
  • 14
7

"i got it working. I just changed models as follows" : quoted from Shruti in the comments above

class Patient < ActiveRecord::Base 
  has_many :appointments, :dependent => :destroy 
  has_many :physicians, :through => :appointments
  accepts_nested_attributes_for :appointments
end 

class Appointment < ActiveRecord::Base
  belongs_to :physician 
  belongs_to :patient
  accepts_nested_attributes_for :physician
end
ctilley79
  • 2,065
  • 3
  • 30
  • 57
4

Your patient class accepts nested attributes for both physicians and appointments. Try adding another fields_for method for appointment.

<% form_for(@patient) do |patient_form| %>
  <%= patient_form.error_messages %>
  <p>
    <%= patient_form.label :name, "Patient Name" %>
    <%= patient_form.text_field :name %>
  </p>

  <% patient_form.fields_for :physicians do |physician_form| %>
    <p>
      <%= physician_form.label :name, "Physician Name" %>
      <%= physician_form.text_field :name %>
    </p>
  <% end %>

  <% patient_form.fields_for :appointments do |appointment_form| %>
    <p>
      <%= appointment_form.label :appointment_date, "Appointment Date" %>
      <%= appointment_form.date_field :appointment_date %>
    </p>
  <% end %>

  <p>
    <%= patient_form.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', patients_path %>
jvperrin
  • 3,322
  • 1
  • 21
  • 33
Andrew K Kirk
  • 301
  • 2
  • 9
  • thanx @andrew for quick reply. I tried code, it results into 2 records getting inserted into appointments table : In 1st record, patient_id & appointment_date gets saved & in 2nd record, physician_id & patient_id gets saved. Any changes required in controller or model? – Wanderer Nov 22 '12 at 07:11
  • @Shruti I think you should remove `@patient.appointments.build`, and in your create action, just use `@patient.save` to save objects. – Thanh Nov 22 '12 at 07:42
  • @kien if I do so, appointment_date text_field is not visible on my form :( – Wanderer Nov 22 '12 at 08:22
  • are you want to build a new physician same time when create appointments? – Thanh Nov 22 '12 at 08:37
  • yes..i want to build New Patient, New Physician & associate new Appointment(alongwith appointment_date) at the same time. – Wanderer Nov 22 '12 at 08:45
  • @Shruti I believe you're creating two separate records because of the two `accepts_nested_attributes_for`. I'm not sure, but do you need both? Because you've also linked Patient and Physician class in the same manner, you may be able to get at appointments by only declare `accepts_nested_attributes_for` once. – Andrew K Kirk Nov 22 '12 at 21:42
  • actually I am really v. confused as to when to use accepts_nested_attributes_for I have modified my patient model 7 now it looks like this : `class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, :through => :appointments # accepts_nested_attributes_for :appointments accepts_nested_attributes_for :physicians end` – Wanderer Nov 23 '12 at 04:31
  • And controller looks like this : `def new @patient = Patient.new @patient.appointments.build end` Parameters : `Parameters: {"authenticity_token"=>"NxtZHoSRSYsrOQhfQk/293qdv7K5WZvbhOFOpMiIzSE=", "patient"=>{"name"=>"abc", "physicians"=>{"name"=>"def"}, "appointments_attributes"=>{"0"=>{"appointment_date"=>"2012-10-12"}}}, "commit"=>"Create"}` This throws an error : `ActiveRecord::AssociationTypeMismatch (Physician(#-627416098) expected, got Array(#67561520)):` – Wanderer Nov 23 '12 at 04:40
  • 3
    i got it working. I just changed models as follows : `class Patient < ActiveRecord::Base has_many :appointments, :dependent => :destroy has_many :physicians, :through => :appointments accepts_nested_attributes_for :appointments end class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patient accepts_nested_attributes_for :physician end` – Wanderer Nov 23 '12 at 12:54
  • 3
    I'm dealing with a similar situation, but I keep running into errors. Would it be possible for you to post the final, working code? Perhaps as an answer? I suspect it will help not just me, but other people dealing with the same problem. – nullnullnull Feb 05 '13 at 04:21