Friday, September 02, 2005

Ruby on Rails - Editing multiple rows of data on one form

I think I may have poorly chosen my first application to learn Rails or maybe not...

I am trying to set up some simple profit-loss reports for our store Natures Paws.

Working with a legacy schema with Rails has been challenging but, I still have found Rails pleasing to use. Tonight, I ran into a situation where, I wanted to be able to update multiple line items in an order and update the model with this data. Rails was able to handle this so easily once I figured out what to do. (Thanks to the params hash dump shown on a Ruby exception page, I was able to figure out what I was doing wrong).

So, if a view has some code, iterating through some line items (each a row in a database table), like so

<% for @line in @profit_report.report_lines %>

<%= h @line.product_name %>
<%= text_field("line[]", 'wholesale_price', :size => 4, :maxsize => 4, :onchange => 'updateWholeSaleTotal(this.value);') %>
<%= text_field("line[]", 'ourshipping_price', :size => 4, :maxsize => 4) %>
<%= h @line.customer_price %>
<%= h @line.customer_shipping_price %>
<%= text_field("line[]", 'usps_price', :size => 4, :maxsize => 4) %>
<%= h @line.commission_paid %>

<% end %>

Then the Controller code can then update each lines in the model like so...

@params[:line].each do |idx, line|
report_line = ReportLine.find(idx)

I have to say that is pretty sweet.


At 5:42 PM, Blogger Diego said...

Hi there.
I have to thank you a lot. I have been trying to get the same kind of functionality done and couldn't figure it out. I am fairly new to both rails and ruby and could not figure out how to get the text_field to show the right info. When I saw "for @item in @itemlist" I was like duuuuuuhhhhhhh
the part that you also helped me was the text_field "item[]" part. I new how to create the output html but not how to have the rails helper do it. :(

So. I have to thank you twice.

At 7:32 PM, Anonymous mathew said...

Thanks for the info.

At 12:17 AM, Anonymous Anonymous said...

I've been trying a similar thing for 3 days now and I'm still stuck.

I have text_field "item[]", 'quantity' as code in my rhtml. And this is for @item in @items where @items is an array.

Now for display this works correctly. All line items are displayed correctly with their respective quantities.

But I dont know why all quantity text boxes on that page get the name item[quantity] and only the 1st one gets passed to the next function. I would expect the text boxes to be named item[0][quantity], item[1][quantity] and so on...

What am i doing wrong??? I wanted to paste the code here but it doesnt allow me to.

At 9:52 AM, Anonymous Anonymous said...

Thank you for describing your approach to multiple-record editing in Rails. I tried applying this technique -- the "line[]", in particular, to a select box and struggled a bit. I was using the select helper method provided in ActionView.

I am editing multiple instances of a "project" domain model.

Here's what ended up working:
<select id="project_<%= %>_organization_id"
name="project[<%= %>][organization_id]">
<%= options_for_select @organizations, project.organization_id %>

At 4:12 PM, Anonymous Anonymous said...

Thanks for the note on the brackets! That's just what I was looking for. It's a bizarre Rails feature, but adding the brackets works.

-Yaron K.

At 9:25 AM, Anonymous Anonymous said...

It's important to note that using item[] will only work if you use @item in the for statement. It will not work if you juse use item.


for @item in @items

Doesn't Work

for item in @items

At 12:04 PM, Anonymous Anonymous said...

quite useful code!

At 1:41 AM, Anonymous Marcello said...

I found your approach working when editing, not when creating active record objects. Possibly it needs ID to be assigned and my apps leave that to the database. And rails is evolving since the blog entry was created. Anyway, here is my approach for newly created AR objects using a partial, supposing you want to render a collection @lineitems:

Initialize a counter to 0 or 1 in the controller or in the view:

@lineitems_counter = 1

create the partial template, increment the counter after form elements:

<%= text_field(:lineitem, :price, 'index' =%> @lineitems_counter ) %>
%lt;% @lineitems_counter += 1 %>

In the "main" form, no need for iteration code, just render the partial for the entire collection:

<%= render(:partial => "insname" , :collection => @insnames ) %>

At 1:44 AM, Anonymous Marcello Della Longa said...

sorry, the %lt;% is meant to be <% - it was a while since i entered html entities on the fly :D

At 10:58 AM, Blogger Mason Kessinger said...

this is just great. worked like a charm!

At 8:07 PM, Blogger derrick said...

Hi, this has gotten me close, but I'm having trouble getting the format exactly right. I'm still a rails novice, and feel a bit in over my head...

The a have table ten rows which is named toppost and has two columns: id and link_id. I would like to be able to edit the link_id from a drop-down, and then have my database updated to account

Here is the code in my rhtml file:
" <% for i in 1..10 %>
<td align=""right""><%= i %></td>
<td align=""left"">
<select name=""toppost[<%= i %>][link_id]"">
<% @links.sort_by{|link| link[:posttime]}.reverse.each do |link| %>
<option value=""<%= %>"">
<%= link.posttime %> -- <%= link.title %>
<% end %>
<% end %>"

What would be the code to replicate what you did? (I've tried, but after 5 hours, my head just hurts.)


At 2:20 PM, Anonymous VK said...

Hi Chris,
Do you have any suggestion for multiple edit form pagination?

Thanks in Advance,

At 2:24 PM, Blogger Eric & Mandy said...

You rock dude. Thanks a bunch!


Post a Comment

Links to this post:

Create a Link

<< Home