views:

50

answers:

1

I'm working on a moderating feature for my application, which is based on a basic scaffold structure. What I need, is to edit several records with the boolean parameter publised on false. In moderate.html I'm getting the list of all unpublished entries with the ability to change their parameters which, what and published. The error appears, when I'm trying to save the changes through the complete action.

ArgumentError in NamesController#complete

Unknown key(s): 7, 1, 4

The "7, 1, 4" are id of my unpublished records.

Here are the parts of my code:

#names_controller.rb

    def moderate
     @names = Name.find(:all, params[:name_ids], :conditions => {:published => false})
      respond_to do |format|
        format.html { render :action => "moderate" }
         format.xml 
       end
     end

def complete
     @names = Name.find(params[:name_ids])
     @names.each do |name|
       name.update_attributes!(params[:name].reject { |k,v| v.blank? })
     end
     flash[:notice] = "Updated records!"
     redirect_to names_path
   end


#moderate.html.erb
<% form_tag complete_names_path do %>  
  <% @names.each do |name| %>
    <fieldset>
    <% fields_for "name_ids[#{name.id}]", name do |name_fields| %>
    <%= name_fields.text_field :which %>
    <%= name_fields.text_field :what %>
    <%= name_fields.check_box :published %> 
    <% end %>
    </fieldset>   
  <% end %>  
<%= submit_tag "Ok" %>
<% end %>/


#routes.rb
ActionController::Routing::Routes.draw do |map| 
  map.connect 'moderate', :controller => 'names', :action => 'moderate'
  map.resources :names, :collection => { :complete => :put}
  map.root :names
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'

I understand, that there's something wrong with the name_ids, but don'nt understand, what should I do. Thank you in advance.

ruby 1.8.7 (2009-06-12 patchlevel 174)

[universal-darwin10.0] Rails 2.3.5

Rails log for moderate and complete actions:

Processing NamesController#moderate (for 127.0.0.1 at 2010-10-16 21:36:42) [GET] [4;35;1mName Load (0.6ms)[0m [0mSELECT * FROM "names" WHERE ("names"."published" = 'f') [0m Rendering template within layouts/names Rendering names/moderate Completed in 12ms (View: 7, DB: 1) | 200 OK [http://localhost/moderate]

Processing NamesController#complete (for 127.0.0.1 at 2010-10-16 21:36:49) [POST] Parameters: {"commit"=>"Ok", "authenticity_token"=>"CtmsjIavksOMSIArrdovkkzuZzHVjkenFFMO5bHIvgg=", "name_ids"=>{"7"=>{"published"=>"0", "what"=>"Партия", "which"=>"Крутая"}, "1"=>{"published"=>"1", "what"=>"Россия", "which"=>"Единая"}, "4"=>{"published"=>"0", "what"=>"Организация", "which"=>"Молдавская"}}}
[4;36;1mName Load (0.4ms)[0m
[0;1mSELECT * FROM "names" WHERE ("names"."id" IN (7,1,4)) [0m

NoMethodError (You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.reject):
app/controllers/names_controller.rb:47:in complete'
app/controllers/names_controller.rb:46:in
each'
app/controllers/names_controller.rb:46:in `complete'

Rendered rescues/_trace (110.3ms) Rendered rescues/_request_and_response (0.5ms) Rendering rescues/layout (internal_server_error)

A: 

Likely you need to get just the keys from the name_ids hash. Try:

@names = Name.find(params[:name_ids].keys)

A separate problem is your reference to params[:name], which is nil. Did you mean (EDIT: use to_s to match the params key, lol):

@names.each do |name|
  name.update_attributes!(params[:name_ids][name.id.to_s].reject { |k,v| v.blank? })
end

EDIT (brief-ish explanation):

What was happening was that you had a nested hash in the params, params[:name_ids]. It looked like:

"name_ids"=>{"7"=>{"published"=>"0", "what"=>"Партия", "which"=>"Крутая"}, "1"=>{"published"=>"1", "what"=>"Россия", "which"=>"Единая"}, "4"=>{"published"=>"0", "what"=>"Организация", "which"=>"Молдавская"}}

An ActiveRecord 'find' method can take an array of ids, but not a hash of values. What you were originally submitting to 'find' in this line:

@names = Name.find(params[:name_ids])

...was the value for params[:name_ids]:

{"7"=>{"published"=>"0", "what"=>"Партия", "which"=>"Крутая"}, "1"=>{"published"=>"1", 
"what"=>"Россия", "which"=>"Единая"}, "4"=>{"published"=>"0", "what"=>"Организация", 
"which"=>"Молдавская"}

When what you wanted was:

@names = Name.find(['7','1','4'])

which is what calling params[:name_ids].keys gives you.

The second problem was this line:

name.update_attributes!(params[:name].reject { |k,v| v.blank? })

There was no value ':name' in params, so calling 'reject' on it cause the 'no method' error -- there is no 'reject' method on the nil object. What you wanted was to update the attributes for the 'name' that corresponded to the particular name in the loop. This meant you wanted to get the values out of params[:name_ids][:id] where :id was the id of 'name'.

It all goes back to the way fields_for created the params to begin with. This line:

<% fields_for "name_ids[#{name.id}]", name do |name_fields| %>

meant that params would contain a hash called 'name_ids', with keys corresponding to name.id, that themselves would contain hashes of attributes that ActiveRecord could use in the update_attributes method.

There's a good bit of the famous Rails magic to keep track of in there -- does that help?

Dave Sims
Is that a line for complete action? It gives me ne error: "NoMethodError in NamesController#completeYou have a nil object when you didn't expect it!You might have expected an instance of Array.The error occurred while evaluating nil.reject"
Anton Axentyuk
That's a different problem. Your params[:name] is coming over nil. (see edit).
Dave Sims
Dave, now my 'complete' action looks like this, but I'm still having the same NoMethpd error. "def complete @names = Name.find(params[:name_ids].keys) @names.each do |name| name.update_attributes!(params[:name_ids][name.id].reject { |k,v| v.blank? }) end flash[:notice] = "Updated records!" redirect_to names_path end"
Anton Axentyuk
can you post the text from your rails log for that action when it fails?
Dave Sims
Sure, its in updated question.
Anton Axentyuk
ha, yeah try .to_s on name.id (see new edit)
Dave Sims
Awesome, now it works! Thx a lot for help! If it's not bothering you, can you explain in few words what was the problem? Just to make it clear for a newbie like me.
Anton Axentyuk
Sure, it will make sense if you look at the params hash that was coming over with the post request. I'll break it down in the answer field (see last edit).
Dave Sims
Ok, thx, now i get it. Yes, that helped.
Anton Axentyuk