doublea doublea - 6 months ago 10
Ruby Question

Why is Backbone model sending duplicate attributes to server on save?

I'm writing a practice Backbone app, with Rails backend API, and I'm confused about the behavior of save on Backbone models.

Let's say a Team has many Players, and I want to save a team with numerous players in a single POST.

So in Rails I have:

class Team < ActiveRecord::Base
has_many :players
accepts_nested_attributes_for :players
end

class Player < ActiveRecod::Base
belongs_to :team
end


and for backbone client, I have a Player model and a Players collection defined (not shown)

and then the containing Team model (NOTE: no Teams collection)

Demo.Models.Team = Backbone.Model.extend({
urlRoot: '/teams',
defaults: {
'team_size': 12
},
initialize: function() {
this.players = new Demo.Collections.Players());
},
toJSON: function() {
var json = _.clone(this.attributes);
json.players_attributes = this.players.map(function(player) {
return player.toJSON();
});
return json;
}
}


When I examine my stringified JSON in the browser, everything looks good:

{"team_size":12, "players_attributes":[{"name":"Fred"},{"name":"Jim" },{"name":"Mark"}]}


Checking the server logs, the lone top level attribute ('team size') is repeated, once at the top level, and then repeated under a root key.

Started POST "/teams" for 127.0.0.1 at 2012-06-07 13:39:40 -0400
Processing by TeamsController#create as JSON
Parameters: {
"team_size"=>12, "players_attributes":[{"name":"Fred"},{"name":"Jim" },{"name":"Mark"}]},
"team"=>{"team_size"=>12}
}


I have a few questions:


  1. What's the best way to ensure the player_attributes are nested inside the root key? I (So that I can do a nested save inside TeamController, in the standard rails manner: (i.e. Team.create(params[:team]) ) I can accomplish this with some javascript hackery inside toJSON, but I'm guessing there's an easier, cleaner way.

  2. Is this standard, desirable behaviour? To send duplicates of attributes like this? I guess there's no harm, but it doesn't smell right.

  3. Am I not defining the url / urlRoot correctly or some such?



thanks

Answer

1- You have to override the toJSON method in order to include the model name as the root of the JSON element sent to the server.

 toJSON: function() {
    return { team: _.clone( this.attributes ) }
  },

Since you are already messing and overriding this method I don't see any reasons not to go this way.

2- This is a very strange behavior you're describing. Try:

class Team < ActiveRecord::Base
  self.include_root_in_json = false
end

It will probably eliminate Rails duplicate params parsing. Another advantage you get from this is that Rails won't include the team as a root element of its generated JSON to the client.

3- Your definition of urlRoot is just fine.

Comments