2

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"}
pedalpete
  • 21,076
  • 45
  • 128
  • 239

2 Answers2

7

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.

salt.racer
  • 21,903
  • 14
  • 44
  • 51
  • But an update always needs the id to be returned, but not updated. I think that was my biggest concern. If I don't send backbone the timestamps, they won't get returned, but I need to send the id. I guess that is why I'm so confused. I can see how rails is 'protecting me' but I'd like to know why I see this in just this occasion. – pedalpete Feb 24 '12 at 19:31
  • Awesome, thanks for your update @salt.racer that makes it clear to me. I wasn't doing a restful update on that model, which I didn't realize until your comment. Now I have the option to go restful or not, and understand why I was getting this response. – pedalpete Feb 24 '12 at 23:44
  • Just a follow up to this — i'm running into the same issue and curious what the "best" solution is, in my case I'm also deleting the ID on update...but there are a bunch of other values that I return as embedded attributes or method returns (like computed labels)...so they can't be updated either. Is there a rails convention to handle this more cleanly? – MBHNYC May 14 '13 at 16:12
4

Those columns are protected by default for mass assignment and cannot be set manually. But, you can override this behavior by defining a method:

def self.attributes_protected_by_default
  [] # ["created_at", "updated_at" ..other]
end

This will allow you to assign created_at and updated_at manually.

Syed Aslam
  • 8,707
  • 5
  • 40
  • 54
  • thanks Syed, I don't want to over-ride them, I'm wondering what might be causing Rails to think that I do? Any ideas? – pedalpete Feb 24 '12 at 15:49
  • The fact that timestamps (created_at, updated_at) and id columns are auto filled in ActiveRecord, if you're sending values for them then you're mass-assigning. – Syed Aslam Feb 24 '12 at 17:11