Search code examples
ruby-on-railsruby-on-rails-3.2nested-formsnested-attributesactivemodel

Nested model form with mutliple has_many/belongs_to associations


I have three models:

class Rate < ActiveRecord::Base
  attr_accessible :user_id, :car_id, :rate

  belongs_to :user
  belongs_to :car
end

class User < ActiveRecord::Base
  attr_accessible :name

  has_many :rates
  accepts_nested_attributes_for :rates
end

class Car < ActiveRecord::Base
  attr_accessible :name

  has_many :rates
  accepts_nested_attributes_for :rates
end

And one controller:

class UsersController < ResourceController
  def new
    # Assume user is loaded
    @user.rates.build
  end
end

I'm trying to build a nested form that will associate a list of users/cars and their associated rates.

Something like:

<% form_for @user do |f| %>
  <%= @user.name %><br />
  <% Car.all.each do |car| %>
    <%= car.name %><br />
    <%= f.fields_for :rates do |r| %>
      <%= r.number_field :rate %>
    <% end %>
  <% end %>
<% end %>

The problem is that I would like the Rate model to store data as follows:

USER_ID    CAR_ID    RATE
1          1         10
1          2         20
1          3         30
2          1         40
3          2         50

I cannot figure out how to properly build the fields_for helper to build the proper params for both the user_id and the car_id.

Something like:

user[car=1][rate]
user[car=2][rate]

I've tried being more explicit with the fields_for like this:

<%= r.fields_for 'user[car][rate]' %>

But it still doesn't build out the nested parameters properly. The car parameter is not correctly identified.

Any help would be appreciated! Thanks.

EDIT: The controller action has to be under user. The example above has been shortened for brevity but other user-related attributes are available through the form so it has to use the users controller.

ANSWER: I figured out a way to do it. I've added my own answer that explains it.


Solution

  • I think I've got it figured out.

    In my controller, I've modified the build method as follows:

    Car.all.each { |c| @user.rates.build(car_id: c.id) } if @user.rates.count == 0
    

    Then, in my model, I need the following:

    attr_accessible :rates_attributes
    

    Finally, the fields_for block should look like this (remember, this is in the @user form object f):

    <%= f.fields_for :rates do |r| %>
      <%= r.hidden_field :car_id %>
      <%= r.object.car.name %><br />
      <%= r.number_field :rate %>
    <% end %>
    

    This builds the params hash properly and create the rate model entries when the form is submitted.

    The check on existing user rates in the controller will ensure that the existing values are used in the form and new ones are not built (which I thought build took into consideration... ?).