views:

137

answers:

1

As far as I can tell, this is a views and controller problem.

I need to allow users to edit multiple records in one of my database tables. Railscast 165 deals with a similar situation, but it's not quite on point for me: http://railscasts.com/episodes/165-edit-multiple

I have a fixed set of products, and I need to let users add associated varieties to those products. I've considered the easier solution of requiring the user to go to a product page, and edit one individual product at a time, but that won't work for my app. Instead, I need to provide more of an admin style functionality for this one task.

The model relationship here is simple. A product has_many varieties, and variety belongs_to product. In my user interface, I simply let the user select a product from a dropdown menu containing all of the available products on the site, and then the user can type the name of a variety in a text input. For example, if we're talking about apples, then the user can select apples from the product dropdown, and he might type in "fuji" and a variety. I simply need to save the product_id and the variety[name] to the varieties table. However, I need to do this for multiple records at one time.

Currently, I've got the form rendering with the dropdown displaying the right product choices. The problem seems to be that the variety input is rendering in html like this:

<label for="nil_class_variety">Variety</label>
<input id="nil_class_name" name="nil_class[name]" size="30" type="text" />

To render parts of the form, I'm following the Advanced Rails Recipes multi-model form technique (i.e. the Ryan Bates way). I spent about 10 hours trying to do this and it seems that I'm now very close, but I don't know what the problem is at this point.

Here's my setup:

Products Controller

def edit_multiple
  @products = Product.find(:all)
end

 def update_multiple
  params[:product][:existing_variety_attributes] ||= {}

   @products = Product.find(:all)
     if @products.each do |product|
       product.update_attributes!(params[:product].reject { |k,v| v.blank? })
     end
     flash[:notice] = "Updated products!"
     redirect_to edit_user_path(self.current_user)
     else
    render :action => 'edit'
    flash[:error] = "Something went wrong.  Please try again."
  end
end

Routes.rb # To make the custom controller actions work

map.resources :products, :collection => { :edit_multiple => :get, :update_multiple => :put}

views/products/edit_multiple.html.erb

<%= error_messages_for :product %>
<% form_for :product, :url => update_multiple_products_path, :html => { :method => :put } do |f| %>

<div id="varieties">
   <%= render :partial => 'variety' %>
</div>

<p><%= add_variety_link "+ Add another variety" %></p>

<%= f.submit 'Submit Varieties' %>
<%end%>

views/products/_variety.html.erb

Note: For now, I'm not letting the users edit the varieties they submit. So I removed the following line from the Ryan Bates technique because it was throwing an error and I don't think I need it, but I could be wrong: <%# new_or_existing = variety.new_record? ? 'new' : 'existing' %>

Here's what I have in this _variety partial:

<% prefix = "product[new_variety_attributes][]" %>
   <% fields_for @variety do |variety_form| -%>
       <%= collection_select(:product, :product_id, Product.all, :id, :name, {:prompt => true}) %>
       <%= variety_form.label :variety %>
       <%= variety_form.text_field :name %>
<%= link_to_function "- Remove Variety", "$(this).up('.variety').remove()" %>
<%end -%>

NOTE: Currently the javascript stuff you see is working. On the edit_multiple view page, I can dynamically add and remove the products/varieties inputs. As I mentioned, the dropdown is also populating properly. So it seems that I just need to get the variety input to render properly and get the controller to properly process it. Thanks for your help!

Update

When I select one product from the dropdown and type a name in the variety input, the submit throws the following error and trace:

 ActiveRecord::UnknownAttributeError in ProductsController#update_multiple
unknown attribute: product_id

/Library/Ruby/Gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2740:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `each'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2736:in `attributes='
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.3/lib/active_record/base.rb:2628:in `update_attributes!'
/Users/michael/dev/fresh/app/controllers/products_controller.rb:74:in `update_multiple'

/Users/michael/dev/fresh/app/controllers/products_controller.rb:73:in each' /Users/michael/dev/fresh/app/controllers/products_controller.rb:73:in update_multiple'

The log shows the correct product_id and the correct name I entered, but you can see it's calling a nil class:

Processing ProductsController#update_multiple (for 127.0.0.1 at 2009-10-08 07:31:05) [PUT]
  Parameters: {"commit"=>"Submit Varieties", "authenticity_token"=>"zzkveSe7qzv2NY8WPrR2cYS376u6DBiz8Vc9iNFLQy8=", "product"=>{"product_id"=>"5"}, "nil_class"=>{"name"=>"yellow"}}

When I try to enter more than one product/variety record, the log isn't recognizing anything except the first one. I get the same result as when I type in just one product/variety record.

A: 

The short answer to your problem, is that @variety is undefined in the fields_for @variety. The correct version of that line in /app/views/products/_variety.html.erb is

<% fields_for :variety do |variety_form| -%>

Also there's a minor nitpick in your label line.

<%= variety_form.label :variety %>

should be

<%= variety_form.label :name, "Variety" %>

I can't tell if your goal is to update multiple products and varieties at once or just update a single product's varieties. Assuming the latter, you should be using accepts_nested_attributes_for (scroll down to the Nested Attributes Example), seems like it might be an easier way to do it. Also see the github Complex-Forms-Example repository for a working demonstration.

But this doesn't seem to be the case. It looks to me that your javascript functions are adding form/removing values for product and varieties.

This will work in the controller for adding varieties, but needs a little more work for removing existing varieties. But the railscasts you linked to provides all the information you need to put that together.

EmFi
Thanks. Still working through this. Will check back in after I implement.
MikeH