Quanrocks11
Quanrocks11

Reputation: 33

Ruby on Rails Nested Models not Updating with form_for, but no error on update

So in my rails project, I have a Patient class, which has one Treatment class. This treatment class then has many DrNotes inside of it. I am still fairly new to rails, and I am aware that nesting this deeply is not recommended in Rails, but I am proceeding with this method.

My problem is with the editing of DrNotes. Since there are many doctor notes within treatment, I am trying to only edit one specific note. I am using Form_for to pass parameters to the doctor's note. When I submit the form, it redirects me to the page that should be shown only when the update function has succeeded. However, none of the notes are actually updated, and no errors are thrown when I try to perform the update.

Here are the models in question:

patient.rb

class Patient < ApplicationRecord
    has_one :treatment, dependent: :destroy

    accepts_nested_attributes_for :treatment, update_only: true

end

treatment.rb

class Treatment < ApplicationRecord
    belongs_to :patient

    has_many :dr_notes, class_name: "DrNote",
        foreign_key: "treatment_id", dependent: :destroy

    accepts_nested_attributes_for :dr_notes
end

dr_note.rb

class DrNote < ApplicationRecord
    belongs_to :treatment
end

In my controller I have:

Doctor Note Edit Function

def edit_dr_note
    @patient = Patient.find(params[:patient_id])
    @dr_note = @patient.treatment.dr_notes.find(params[:dr_id])
    @dr_note.update if @dr_note.nil?
end

Doctor Note Update Function

def update_dr_note
    @patient = Patient.find(params[:patient_id])
    @dr_note = @patient.treatment.dr_notes.find(params[:dr_id])
    if @dr_note.update(dr_note_params)
        redirect_to page_path(@patient)
    else
        flash.now[:error] = "Cannot update Doctor's notes"
        render 'edit_dr_note'
    end
end

Doctor Note Params

def dr_note_params
    params.require(:dr_note).permit(:id, :name, :message)
end

I have :id in the params.permit because from researching, I heard that you need to include it when updating models, but i'm not sure if it is needed here.

I have the following code in the routes.rb

get '/pages/:patient_id/treatment/edit/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "pages/:patient_id/treatment/update/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]

And in the edit_dr_note.html.erb

<%= form_for @patient.treatment.dr_notes.find(params[:dr_id]), url: update_dr_note_path do |patient_form| %>

    <% @patient.treatment.dr_notes.each do |doctor| %>
    <% if doctor.id == @dr_note.id %>   #Only displays the fields for the desired note
        <%= patient_form.fields_for :dr_note, doctor do |doctor_fields| %>
            Name: <%= doctor_fields.text_field :name %>
            Message: <%= doctor_fields.text_field :message %>
        <% end %>
        <p>
            <%= patient_form.submit %>
        </p>
        <% end %>
    <% end %>
<% end %>

Any help would be greatly appreciated. Thanks!

Upvotes: 3

Views: 55

Answers (2)

Pavan
Pavan

Reputation: 33552

You are mixing two approaches(the nested resources and the nested attributes). Use one to serve your purpose.

With the nested resources:

<%= form_for [:pages, @patient, @treatment, @dr_note], url: update_dr_note_path do |dr_note| %>
  Name: <%= dr_note.text_field :name %>
  Message: <%= dr_note.text_field :message %>
  <p>
    <%= dr_note.submit %>
  </p>
<% end %>

The routes would be

get '/pages/:patient_id/treatment/:treatment_id/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "pages/:patient_id/treatment/:treatment_id/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]

Edit the edit_dr_note to define @treatment

def edit_dr_note
  @patient = Patient.find(params[:patient_id])
  @treatment = @patient.treatment
  @dr_note = @patient.treatment.dr_notes.find(params[:dr_id])
  @dr_note.update if @dr_note.nil?
end

And finally remove accepts_nested_attribute_for from the models, you don't need it in this approach.

With the nested attributes:

Keep the accepts_nested_attributes_for in the models. And change the routes and form like below

get '/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]

And the form_for

<%= form_for @patient, url: update_dr_note_path do |patient| %>
  <%= patient.fields_for :treatment do |t| %>
    <%= t.fields_for :dr_notes, @dr_note do |dr_note| %>
      Name: <%= dr_note.text_field :name %>
      Message: <%= dr_notetext_field :message %>
    <% end %>
  <% end %>
  <p>
    <%= patient.submit %>
  </p>
<% end %>

And change the dr_note_params method as below

def dr_note_params
  params.require(:patient).permit(:id, treatment_attributes: [:id, dr_notes_attributes: [:id, :name, :message])
end

Upvotes: 1

Jaffa
Jaffa

Reputation: 12719

When you write the following line, you're trying to find a DrNote using the dr_id:

@dr_note = @patient.treatment.dr_notes.find(params[:dr_id])

Whereas the dr_notes relation on Treatment does not seem to define any particular behavior, and this is your problem.

You'll need to find_by doctor's id (or dr_id in your code) and thus first define the relation on DrNote.

Upvotes: 0

Related Questions