Search code examples
ruby-on-railsnested-attributeshas-many-through

Rails - Issue in has_many through with nested attributes


I am having issue with saving a has_many through relation with nested attributes. Due to complexity and requirment in the application the relation is as follows

Table structure,

agreements:
  id

agreement_rooms:
  id
  agreement_id
  room_id

details:
  id
  agreement_rooms_id

For more clarification, agreement_rooms table is related to many other models which will be having agreement_rooms_id in them.

Rails Associations,

class Agreement < ActiveRecord::Base
  has_many :details,:through => :agreement_rooms
  accepts_nested_attributes_for :details
end

class AgreementRoom < ActiveRecord::Base
  has_many :details
end

class Detail < ActiveRecord::Base
  belongs_to :agreement_room
  accepts_nested_attributes_for :agreement_room
end

When i try to create a agreements record with details hash in it, i get the following error,

Agreement.last.details.create()

ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'agreement#details' because the source reflection class 'Detail' is associated to 'agreementRoom' via :has_many.

I am not sure how to get this nested attributed working with has_many through relation for the above example. Please help out to figure the issue.

Thanks in advance.


Solution

  • #app/models/aggreement.rb
    class Agreement < ActiveRecord::Base
       has_many :agreement_rooms
       accepts_nested_attributes_for :agreement_rooms
    end
    
    #app/models/agreement_room.rb
    class AgreementRoom < ActiveRecord::Base
       belongs_to :agreement
       belongs_to :room
    
       has_many :details
       accepts_nested_attributes_for :details
    end
    
    #app/models/room.rb
    class Room < ActiveRecord::Base
       has_many :agreement_rooms
       has_many :agreements, through: :agreement_rooms
    end
    
    #app/models/detail.rb
    class Detail < ActiveRecord::Base
       belongs_to :agreement_room
    end
    

    --

    #app/controllers/agreements_controller.rb
    class AgreementsController < ApplicationController
       def new
          @agreement = Agreement.new
          @agreement.agreement_rooms.build.details.build
       end
    
       def create
          @agreement = Agreement.new agreement_params
          @agreement.save
       end
    
       private
    
       def agreement_params
          params.require(:agreement).permit(:agreement, :param, agreement_rooms_attributes: [ details_attributes: [:x] ])
       end
    end
    
    #app/views/agreements/new.html.erb
    <%= form_for @agreement do |f| %>
       <%= f.fields_for :agreement_rooms do |ar| %>
          <%= ar.fields_for :details do |d| %>
             <%= d.text_field :x %>
          <% end %>
       <% end %>
       <%= f.submit %>
    <% end %>