Rails can't mass-assign protected attributes for id, created_at

pedalpete picture pedalpete · Feb 24, 2012 · Viewed 7.4k times · Source

I'm using rails as the server side of a backbone.js site, therefore I'm constantly passing the rails objects back and forth.

I'm noticing errors in rails returning WARNING: Can't mass-assign protected attributes: id, created_at, updated_at.

Of course, I find this strange because I've never had to include these fields My json looks fairly normal as far as I can tell

 Parameters: {"id"=>1, "updated_at"=>"2011-04-21T16:41:02Z"},  "created_at"=>"2012-02-23T21:01:02Z", "action"=>"test update"}

Answer

salt.racer picture salt.racer · Feb 24, 2012

There is nothing wrong with your JSON. The issue is one of security. Rails protects certain attributes by default from being created or updated from a giant hash. This is what the error is referring to when it uses the term "mass-assignment."

The JSON you posted:

Parameters: {"id"=>1, "updated_at"=>"2011-04-21T16:41:02Z"}, "created_at"=>"2012-02-23T21:01:02Z", "action"=>"test update"}

contains the id the created_at and the updated_at fields. When this JSON is passed into the action and the hash is used in a model_object.update_attributes(hash_fields) you will get this error. To avoid this error you can delete the fields from the hash and assign them later, or ideally, let ActiveRecord work it's magic for you and just ignore them.

If you really do need to assign them you can do that like:

 model_object.id = id_variable
 model_object.created_at = created_at_variable
 model_object.updated_at = updated_at_variable
 model_object.save

EDIT1 (to address the comment about passing back the id):

If you are using the Rails REST model and calling the controller/:id/action url, you don't need to pass the ID back, as that information is already embedded in the URL. It can be accessed via params[:id] and the hash via params[:model_name] (following the Rails model).

If you are doing something different and the ID must be in the JSON being passed back then you can simply do id = params[:model_name][:id].delete and that will delete the id from the hash and return the value in one call. It's not ideal, but it can get the job done in a pinch.