4

I have Backbone.js collection and model for a project object:

window.Project = Backbone.Model.extend();

window.Projects = Backbone.Collection.extend({
    model: Project, 
    url: '/projects'
});

I have setup a rails controller to respond to the Backbone.js collection:

class ProjectsController < ApplicationController

    def index
        render :json => Project.all
    end

    def create
        project = Project.create! params
        render :json => project
    end

end

Index works fine and I get a list of projects in my web app. The problem is if I try and create a model on the Projects collection I get a 500 error from the server.

The error message on the server is as follows:

Started POST "/projects" for 127.0.0.1 at 2011-08-21 08:27:56 +0100
  Processing by ProjectsController#create as JSON
  Parameters: {"title"=>"another test"}
Completed 500 Internal Server Error in 16ms

ActiveRecord::UnknownAttributeError (unknown attribute: action):
  app/controllers/projects_controller.rb:8:in `create'

I am not sure what the unknown attribute: action is referring to.

For info I have set up the projects_controller as resources :projects. I have also set rails to ActiveRecord::Base.include_root_in_json = false.

1
  • Thanks, So Rails always adds action and controller to params. That would explain the error. Commented Aug 21, 2011 at 9:11

2 Answers 2

5

Yes, Rails always adds the action and controller to params. The parameters come from ActionDispatch::Http::Parameters:

def parameters
  @env["action_dispatch.request.parameters"] ||= begin
    params = request_parameters.merge(query_parameters)
    params.merge!(path_parameters)
    encode_params(params).with_indifferent_access
  end
end

And path_parameters:

Returns a hash with the parameters used to form the path of the request. Returned hash keys are strings:

{'action' => 'my_action', 'controller' => 'my_controller'}

So you shouldn't be doing project = Project.create! params. You could go the update_attributes route:

project = Project.new
project.update_attributes params[:model_name]

But this assumes that you have what you need in a sub-hash of params and it won't call your validators. Backbone won't namespace your attributes by default but you could override Backbone.sync and do it yourself. Still, you probably want your validations so update_attributes should generally be avoided.

Your best bet is to pull exactly the attributes out of params that you're expecting to be there. This is even the Backbone recommended practise:

*(In real code, never use update_attributes blindly, and always whitelist the attributes you allow to be changed.)*

Sign up to request clarification or add additional context in comments.

5 Comments

Yes, thanks for the explanation. This certainly seems to make using Backbone.js start to feel pretty clunky, having to pull out just the params I'm expecting becomes a maintenance problem.
@shakerlxxv: You could easily filter params if you don't want your controllers to control their own interfaces; you can dup and delete to get rid of the things you don't want or slice to get just what you're expecting. Your model validations would, of course, have to be very detailed and paranoid in such situations.
forgot to mention, the update_attributes approach did not work for me, still produced the UnknownAttributeError
@shakerlxxv: Looks like update_attributes pretty much assumes that you'll update_attributes params[:stuff] and is too dumb to only look for things that should be there. I never use it as I like to have clear and specified interfaces and I'm a bit disappointed that it is so simple minded. Presumably moose56 went the "pull exactly the attributes out of params that you're expecting to be there" route, I'll update the answer for posterity.
My first step was to use the info @mu-is-too-short gave to just get it working in the easiest way I could, by deleting the action and controller params from the hash. Once it was all working and I had a better idea of what was happening I updated it to pull out only the params that I needed.
0

You can enable parameter wrapping. Add a file in the initializer directory with:

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end

and, for json request, you post params will now be wrapped with the model name.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.