Search code examples
javascriptruby-on-railsrubybackbone.jsnested-attributes

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


Solution

  • 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.