RickyD
RickyD

Reputation: 401

Update and create multiple records in a single form in Rails 3

I have followed railscast #198 Edit Multiple Individually and it works 100% for editing multiple records from a single form.

However, I would like to also create new records from the same form if the record. i.e. in the controller assess the hash of params from the form, update those records that exist and create the records that don't exist.

Also, if any validation errors occur they should be caught and passed back.

This in the controller works and will update correctly if the fields for the new record aren't present @costs = IngredientCost.update(params[:ingredient_costs].keys, params[:ingredient_costs].values).reject { |item| item.errors.empty? }

For the fields for the new records I am currently creating an artibrary large unique number as the record id (I'm not sure if this is right to do) and then the message comes back that it cannot be saved because the id doesn't exist, which is to be expected as I it doesn't exist.

I've tried the following two snippets as a stab in the dark but not really sure which way I should be going..

@costs = IngredientCost.find_or_initialize_by_id(:id => params[:ingredient_costs].keys).update(params[:ingredient_costs].keys, params[:ingredient_costs].values).reject { |i| i.errors.empty? }

and

params[:ingredient_costs].each do |ingredient_cost|
  @cost = IngredientCost.find_or_initialize_by_ingredient_id_and_user_id(:ingredient_id => ingredient_cost[1]["ingredient_id"], :user_id => current_user.id)
  @cost = IngredientCost.update(:ingredient_id => params[:ingredient_costs][ingredient_cost[0]]["ingredient_id"], :quantity => params[:ingredient_costs][ingredient_cost[0]]["quantity"], :price_per_unit => params[:ingredient_costs][ingredient_cost[0]]["price_per_unit"], :weight_id => params[:ingredient_costs][ingredient_cost[0]]["weight_id"])

Neither seem to work.

I've seen this work before but for updating/creating nested attributes from one form. I don't really want to have to go that route with this.

Upvotes: 2

Views: 5818

Answers (2)

RickyD
RickyD

Reputation: 401

I eventually resorted to saving the multiple records through a parent model which already had the correct associations.

I followed Railscast #196 Nested Model Forms which was fairly similar to above only saving to another model and placing accepts_nested_attributes_for in that model's *.rb.

Remember to move the routes.

Move the controller code to the receiving action of the parent model.

My form in the viewer now looks like this (in haml format):

= form_for @user, :remote => true do |f|
  = f.fields_for :ingredient_costs do |builder|
    // fields inserted here

It took me a couple hours to realise I had to use builder.object... to get to the child that I actually am interested. The .object class wasn't obvious to me.

Anyway, hope this was useful to someone. I think SybariteManoj's answer looks like a good deal, but I couldn't get it to work.

Upvotes: 3

Manoj Monga
Manoj Monga

Reputation: 3093

You can try following snippet:

@costs = []
params[:ingredient_costs].each do |ingredient_cost|
  if ingredient_cost[:id].blank?
    cost = IngredientCost.create(ingredient_cost)
    @costs << cost
  else
    cost = IngredientCost.find(ingredient_cost[:id])
    cost.update_attributes(ingredient_cost)
    @costs << cost
  end
end

P.S. I haven't tested it yet. But for the re-rendering the edit page with the errors, the @costs variable has the data with errors if any.

Upvotes: 1

Related Questions