0

EDITED: code below reflects last suggestions from your answers...

I'm having trouble with saving to two different models, from one form... I tried to follow rails casts #196 and looked at several other examples here at stackoverflow but I haven't figured out how to do this because my case is a little bit different...

So, I have two models:

class Patient < ActiveRecord::Base
  attr_accessible :date_of_birth, :patient_name

  has_many :samples
end

class Sample < ActiveRecord::Base
  attr_accessible :approved, :patientID, :result, patient_attributes: [:patient_name, :date_of_birth]

  belongs_to :patient
  accepts_nested_attributes_for :patient
end

What I found on most of the examples I saw, including the railscasts, is to create a new sample inside the patient form, and the "accepts_nestes_attributes_for" is inside the "patient" model... But what I want to do is exactly the opposite, that is, creating a new patient inside the new sample form.

Here're my views, I have a normal form for the sample and then a partial for the patient part:

_form.html.erb

<%= form_for(@sample) do |f| %>
  <% if @sample.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@sample.errors.count, "error") %> prohibited this sample from being saved:</h2>

      <ul>
      <% @sample.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :result %><br />
    <%= f.text_area :result %>
  </div>
  <div class="field">
    <%= f.label :approved %><br />
    <%= f.check_box :approved %>
  </div>
  <div><%= render 'patient', f: f %></div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

patient.html.erb

<%= f.fields_for :patient do |p| %>
<div class="field">
    <%= p.label :patient_name %><br />
    <%= p.text_field :patient_name %>
</div>
<div class="field">
    <%= p.label :date_of_birth %><br />
    <%= p.date_select :date_of_birth %>
</div>
<% end %>

When I click the submit button everything seems to work, but nothing is saved into the patients table.

Here's the log:

Started POST "/samples" for 127.0.0.1 at 2013-10-12 23:32:03 +0100
Processing by SamplesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"13OKZ8DaGJ4zTb35q+ymSzx7r+Ipxou1u+XrR4jtyeI=", "sample"=>{"result"=>"teste 3", "approved"=>"0"}, "patient"=>{"patient_name"=>"Joao", "date_of_birth(1i)"=>"2013", "date_of_birth(2i)"=>"10", "date_of_birth(3i)"=>"10"}, "commit"=>"Create Sample"}
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "samples" ("approved", "created_at", "patientID", "result", "updated_at") VALUES (?, ?, ?, ?, ?)  [["approved", false], ["created_at", Sat, 12 Oct 2013 22:32:03 UTC +00:00], ["patientID", nil], ["result", "teste 3"], ["updated_at", Sat, 12 Oct 2013 22:32:03 UTC +00:00]]
   (2.4ms)  commit transaction
Redirected to http://localhost:3000/samples/3
Completed 302 Found in 6ms (ActiveRecord: 2.9ms)

As you can see in the log, it's only saving to the sample table. Can I use accepts_nested_attributes_for this way? Is there a way to achieve what I want? Or should I try a different approach?

UPDATE: Here's the code for my patient and sample controllers:

patients_controller.rb

def new
    @patient = Patient.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @patient }
    end
end

def create
    @patient = Patient.new(params[:patient])

    respond_to do |format|
      if @patient.save
        format.html { redirect_to @patient, notice: 'Patient was successfully created.' }
        format.json { render json: @patient, status: :created, location: @patient }
      else
        format.html { render action: "new" }
        format.json { render json: @patient.errors, status: :unprocessable_entity }
      end
    end
end

samples_controller.rb

def new
    @sample = Sample.new
    @sample.build_patient #suggested by El Key

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @sample }
    end
end

def create
    @sample = Sample.new(params[:sample])

    respond_to do |format|
      if @sample.save
        format.html { redirect_to @sample, notice: 'Sample was successfully created.' }
        format.json { render json: @sample, status: :created, location: @sample }
      else
        format.html { render action: "new" }
        format.json { render json: @sample.errors, status: :unprocessable_entity }
      end
    end
end

Here's the updated log after I follow El Key suggestions:

Started POST "/samples" for 127.0.0.1 at 2013-10-17 00:20:05 +0100
Processing by SamplesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"NZXQUdd8tQc206LdEuYF5iO5+89wlfza0VbbvgBGNuI=", "sample"=>{"result"=>"teste 9", "patientID"=>"", "approved"=>"0", "patient_attributes"=>{"patient_name"=>"Sergio", "date_of_birth(1i)"=>"2013", "date_of_birth(2i)"=>"10", "date_of_birth(3i)"=>"2"}}, "commit"=>"Create Sample"}
Completed 500 Internal Server Error in 1ms

ActiveModel::MassAssignmentSecurity::Error - Can't mass-assign protected attributes: patient_attributes:

As you can see, now I have a problem for "can't mass-assign protected attributes", but shouldn't this work as I have the "patient_attributes" on the sample model?

After I solve this I will try to check first if the patient exists or not before committing and try to do as SAYT on the patient's name... but that's a future step!

Thanks for your help

After following El-Key advise and suggestions, I found the solution

Update, Final Code - My solution

models/samples.rb

class Sample < ActiveRecord::Base
  attr_accessible :approved, :patient_id, :result, :patient_attributes

  belongs_to :patient
  accepts_nested_attributes_for :patient
end

models/patient.rb

class Patient < ActiveRecord::Base
  attr_accessible :date_of_birth, :patient_name 
  has_many :samples
end

controllers/samples_controller

def new
    @sample = Sample.new
    @sample.build_patient

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @sample }
    end
end

def create
    @sample = Sample.new(params[:sample])

    respond_to do |format|
      if @sample.save
        format.html { redirect_to @sample, notice: 'Sample was successfully created.' }
        format.json { render json: @sample, status: :created, location: @sample }
      else
        format.html { render action: "new" }
        format.json { render json: @sample.errors, status: :unprocessable_entity }
      end
    end
end

views/samples/_form.html.erb

<%= form_for(@sample) do |f| %>
  <% if @sample.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@sample.errors.count, "error") %> prohibited this sample from being saved:</h2>

      <ul>
      <% @sample.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :result %><br />
    <%= f.text_area :result %>
  </div>
    <div class="field">
        <%= f.label :patient_id, 'Patient ID' %><br />
        <%= f.text_area :patient_id %>
    </div>
  <div class="field">
    <%= f.label :approved %><br />
    <%= f.check_box :approved %>
  </div>

  <div><%= render 'patient', f: f %></div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

views/samples/_patient.html.erb

<%= f.fields_for :patient do |p| %>
<div class="field">
       <%= p.label :patient_name %><br />
       <%= p.text_field :patient_name %>
</div>
<div class="field">
       <%= p.label :date_of_birth %><br />
       <%= p.date_select :date_of_birth %>
</div>
<% end %>
lbramos
  • 327
  • 1
  • 6
  • 19

2 Answers2

2

The fields_for method has to be call from the parent form, such as :

_form.html.erb

<div><%= render 'patient', f: f %></div>

patient.html.erb

<%= f.fields_for :patient do |p| %>
  <!-- code -->
<% end %>

Try this. If it's still not working, could you please show us your controller. Hope this help

EDIT
Ok then try to replace in your Sample model patient by patient_attributes as :

attr_accessible patient_attributes: [:patient_name, :date_of_birth]
lkartono
  • 2,323
  • 4
  • 29
  • 47
  • Thanks a lot for your reply! I tried your code but what happened was that the fields on the "_patient.html.erb" file stop appearing on the webpage. It's weird, because we're not messing with the "<%= >" signs... any idea why this might happen? – lbramos Oct 14 '13 at 22:47
  • In your controller, you have then to create an empty record for patient on `new` action as : `@sample.patient.build` – lkartono Oct 14 '13 at 23:39
  • I did @sample.build_patient and the fields appeared. But I still have problems... I've updated the questions to show this and the new log... – lbramos Oct 14 '13 at 23:48
  • Into your Sample model try to add the fields you want to save on patient such as : `attr_accessible :approved, :patientID, :result, patient: [:patient_name, :date_of_birth]` – lkartono Oct 15 '13 at 03:47
  • Still no good... It still goes to the samples controller and only saves to the database de sample fields, it doesn't even tries to save to the patient model... any more ideas? Could it be something on the routes? should I add something more to the samples controller create or new action? – lbramos Oct 15 '13 at 23:31
  • Ok then try to replace in your Sample model `patient` by `patient_attributes` as : `patient_attributes: [:patient_name, :date_of_birth]` – lkartono Oct 16 '13 at 00:06
  • Still no luck... same problem, I've updated the log on the main question... thanks a lot for keep trying! I really thought this could be much easier... – lbramos Oct 16 '13 at 00:41
  • I did it the exact same way on my app and it's working. The only difference is that I'm using Rails 4 and I'm using strong parameters : `params.require(:sample).permit(patient_attributes: [:patient_name, :date_of_birth])` – lkartono Oct 16 '13 at 03:49
  • There must be a way to do this on rails 3... I'll digg a little more :) – lbramos Oct 16 '13 at 09:46
  • Hi El-Key, I was missing the <%= render 'patient', f: f %>, but now I've got a "can't mass assign error"... I've updated all the code above, I think we're getting closer :) – lbramos Oct 16 '13 at 23:28
2
  1. If you have patientID which is a different field and not the foreign key in samples table, you have to make it available via , :patient_id or leave it if patientID is meant for a different purpose.

  2. you need to give an new object for the fields for

Change your sample model like this

attr_accessible :approved, :patientID, :result, :patient_attributes

And sample form line from

<div><%= render 'patient', f: f %></div>

to

<div>
<%= f.fields_for :patient, Patient.new do |p| %>
  <%= p.text_field :name %>
  <%= p.text_field :date_of_birth %>
<% end %>
</div>

I've created a sample app for you to understand how it works.

https://github.com/ravensnowbird/inverse_nested

You can remove @sample.build_patient as well.

Vivek Sampara
  • 447
  • 4
  • 20
  • Hi, I already got it working, I'm going to put the final code on my initial question. I followed El-Key advise and I got it working. You're right about the "patientID" on the Sample Mode. I've changed it to "patient_id" and now it stores also the correct patient_id on the samples model, thanks for noticing that! I will anyhow mark El-Key answer as correct because it was the one who brought me closer to the solution! Thanks for all the work on providing the sample app – lbramos Oct 17 '13 at 10:36