Search code examples
ruby-on-rails-3nested-attributesnested-form-for

How does a has_many :through association and its join model attribute interact?


I am having real difficulty getting my head around the issue of setting the attribute in my join model?

My models:

  class Contract < AR::Base 
    has_many :codelines
    has_many :codes, :through => :codelines

    accepts_nested_attributes_for :codes

    attr_accessible :codes_attributes, :codes; :authnum, :st_date, :end_date
  end

  class Codeline < AR::Base
    belongs_to :contract
    belongs_to :code
    units_alloc ...... this is the attribute I would like to set
  end

  class Code < AR::Base
    has_many :codelines
    has_many :contracts, :through => :codelines
  end

The new action of my app/controllers/contracts_controller.rb

  def new
    @contract = Contract.new
    @contract.codes.build
  end

the partial for my view in app/views/contracts/_fields.html.haml

  <fieldset><legend>Enter Billing Code Details</legend>
  = f.fields_for :codes do |ff|
     .field
        = ff.label :name, "Code Name"
        %br/
        = ff.text_field :code_name
     .field
     .
     .
   = f.fields_for :codelines do |ff|
     .field
       = ff.label :name, "Units Alloc"
       %br/
       = ff.text_field :units_alloc, :precision => 6, :scale => 2, :size => 10
  </fieldset>

From reading Rails Guides and watching railscasts #196 and #197 and researching nested attributes on the internet I came to the understanding that the @contract.codes.build line in the new action of my app/controllers/contracts_controller.rb not only built the code object but that it also built the codelines object. If I leave my new action of my app/controllers/contracts_controller.rb as above that is exactly what happens. My codelines table is populated as follows:

    id       contract_id       code_id       units_alloc
     1             1                1                  .... @contract.codes.build

But if you look at my view I would actually like to set units_alloc and this line @contract.codes.build does not give me access to units_alloc since it is in the codeline model. I added @contract.codelines.build to the new action of my app/controllers/contracts_controller.rb and now the units_alloc shows in my view and I am able to set it. But now I have two rows in my codelines table. One resulting from the @contract.codes.build where both the code object and codeline object are built and the second row is as a result of the @contract.codelines.build and my codelines table is as follows:

    id       contract_id       code_id       units_alloc
     1             1                1                  .... @contract.codes.build
     2             1                             80.00 .... @contract.codelines.build

Should'nt I be able to get access to units_alloc through the first build given by @contract.codes.build, where both the code object and the codeline object is built?

Does anyone know if my understanding of this issue is correct or could you point me to a resource where all will be revealed?

Just as an after thought I built the code object through codelines and I got the same result.

Thanks for any suggestions.

Update I can get my codelines table to have only one record with all the associated id's set as follows:

My console:

    @contract = Contract.new(authnum: "900700", st_date: "2012-01-01",  end_date:
    "2012-30-06")

    @contract.save

    @code = Code.new(code_name: "S-5463", status: "Active",   description: 
    "This and That")

    @code.save

    @codeline = @code.codelines.build(:units_alloc => "80.00",  :contract => @contract)

    @codeline.save

    @codeline
    => #<Codeline id: 91, contract_id: 64, code_id: 54, units_alloc: 80.00>

Using pgadmin3 I check my codelines table and I get just one record, namely:

    id    contract_id    code_id   units_alloc
    91         64            54       80.00

Now the test is to get my contracts_controller new/create action to do the same.


Solution

  • I found a solution at this site, debugging nested_forms.

    It is the ninth bullet down where you read that if you are working with a has_many :through association then you need to base your nested_form on the join model or something close to that.

    I quickly ran a test by refactoring some code here and there and I now have a workable nested form that serves up the correct params to the controller which in turns processes it correctly and my codelines table now has just one record.