diff --git a/README.md b/README.md index 818c835f..5e1fa848 100644 --- a/README.md +++ b/README.md @@ -146,13 +146,27 @@ For example, a Rails backend may provide the keys suffixed with `_id` or `_ids`. 1. When a relation is instantiated, the contents of the `keySource` are used as it's initial data. 2. The application uses the regular `key` attribute to interface with the relation and the models in it; the `keySource` is not available as an attribute for the model. -3. When calling `toJSON` on a model (either via `Backbone.sync`, or directly), the data in the `key` attribute is tranformed and assigned to the `keySource`. So you may be provided with data containing `animal_ids`, while you want to access this relation as `zoo.get( 'animals' );`. -When saving `zoo`, the `animals` attribute will be serialized back into the `animal_ids` key. + +**NOTE**: for backward compatibility reasons, setting `keySource` will set `keyDestination` as well. +This means that when saving `zoo`, the `animals` attribute will be serialized back into the `animal_ids` key. **WARNING**: when using a `keySource`, you should refrain from using that attribute name for other purposes. +### keyDestination + +Value: a string. References an attribute to serialize `relatedModel` into. + +Used to override `key` (and `keySource`) when determining what attribute to be written into when serializing a relation, since the server backing your relations may use different naming conventions. +For example, a Rails backend may expect the keys to be suffixed with `_attributes` for nested attributes. + +When calling `toJSON` on a model (either via `Backbone.sync`, or directly), the data in the `key` attribute is transformed and assigned to the `keyDestination`. + +So you may want a relation to be serialized into the `animals_attributes` key, while you want to access this relation as `zoo.get( 'animals' );`. + +**WARNING**: when using a `keyDestination`, you should refrain from using that attribute name for other purposes. + ### collectionType Value: a string (which can be resolved to an object type on the global scope), or a reference to a `Backbone.Collection` type. diff --git a/backbone-relational.js b/backbone-relational.js index 5fff4bca..8e9d9820 100644 --- a/backbone-relational.js +++ b/backbone-relational.js @@ -276,6 +276,7 @@ this.key = this.options.key; this.keySource = this.options.keySource || this.key; + this.keyDestination = this.options.keyDestination || this.options.keySource || this.key; // 'exports' should be the global object where 'relatedModel' can be found on if given as a string. this.relatedModel = this.options.relatedModel; @@ -1202,21 +1203,21 @@ var value = json[ rel.key ]; if ( rel.options.includeInJSON === true && value && _.isFunction( value.toJSON ) ) { - json[ rel.keySource ] = value.toJSON(); + json[ rel.keyDestination ] = value.toJSON(); } else if ( _.isString( rel.options.includeInJSON ) ) { if ( value instanceof Backbone.Collection ) { - json[ rel.keySource ] = value.pluck( rel.options.includeInJSON ); + json[ rel.keyDestination ] = value.pluck( rel.options.includeInJSON ); } else if ( value instanceof Backbone.Model ) { - json[ rel.keySource ] = value.get( rel.options.includeInJSON ); + json[ rel.keyDestination ] = value.get( rel.options.includeInJSON ); } } else { delete json[ rel.key ]; } - if ( rel.keySource !== rel.key ) { + if ( rel.keyDestination !== rel.key ) { delete json[ rel.key ]; } }, this ); diff --git a/test/tests.js b/test/tests.js index e576be95..dde1981c 100644 --- a/test/tests.js +++ b/test/tests.js @@ -649,7 +649,7 @@ $(document).ready(function() { ok( person.get( 'user' ).get( 'resource_uri' ) == null ); }); - test( "'keySource' loads from & saves to 'key'", function() { + test( "'keySource' loads from 'key", function() { var Property = Backbone.RelationalModel.extend({ idAttribute: 'property_id' }); @@ -689,12 +689,49 @@ $(document).ready(function() { // The values from view.property_ids should be loaded into view.properties ok( view.get( 'properties' ) && view.get( 'properties' ).length === 2, "'view' has two 'properties'" ); ok( typeof view.get( 'property_ids' ) === 'undefined', "'view' does not have 'property_ids'" ); + }); + + test( "'keyDestination' saves to 'key'", function() { + var Property = Backbone.RelationalModel.extend({ + idAttribute: 'property_id' + }); + var View = Backbone.RelationalModel.extend({ + idAttribute: 'id', + + relations: [{ + type: Backbone.HasMany, + key: 'properties', + keyDestination: 'properties_attributes', + relatedModel: Property, + reverseRelation: { + key: 'view', + keyDestination: 'view_attributes', + includeInJSON: true + } + }] + }); + + var property1 = new Property({ + property_id: 1, + key: 'width', + value: 500, + view: 5 + }); + + var view = new View({ + id: 5, + properties: [ 2 ] + }); + + var property2 = new Property({ + property_id: 2, + key: 'height', + value: 400 + }); var viewJSON = view.toJSON(); - ok( viewJSON.property_ids && viewJSON.property_ids.length === 2, "'viewJSON' has two 'property_ids'" ); + ok( viewJSON.properties_attributes && viewJSON.properties_attributes.length === 2, "'viewJSON' has two 'properties_attributes'" ); ok( typeof viewJSON.properties === 'undefined', "'viewJSON' does not have 'properties'" ); - - console.log( view, viewJSON, property1, property2 ); }); test( "'collectionOptionsCallback' sets the options on the created HasMany Collections", function() {