backbone-associations icon indicating copy to clipboard operation
backbone-associations copied to clipboard

Different keys for lookup and relation referencing

Open soswow opened this issue 10 years ago • 3 comments

I am thinking about similar attributes Relational has key and keySource This perfectly matched our data/server communication before.

Example. Here is project object

{"id": 10, "name": "Project"}

And this is task:

{"id": 20, "name": "Task", "pid": 10}

pid - is id of a project this task belongs to. So I naturally do something like this

class Task extends Backbone.AssociatedModel
  relations: [{
    type: Backbone.One
    relatedModel: 'model.Project'
    key: 'pid'
    map: (pid) ->
      someGlobalScope.projects.get pid
  }]

But now, I can access project from Task only by task.get('pid') - But this is wrong. pid is Project Id, not project itself. I would like to task.get('project')

Is there a way?

soswow avatar Jul 17 '14 11:07 soswow

Don't know if this is should be requested here, but ... In the example above, when we want to store Task back to the server, we want to send information about project it belongs to. And this is also defined with pid property. Right now I can define serialize: ['id'], but this will make

{"id": 20, "name": "Task", "pid": {"id": 10}}

which is not what I wanted =\ I thought what if serialize could be not only Array, but a string also. In case of string it could make what I want for example. Also this will makes IN and OUT to be consisted (Which is logical I think) (I mean what was passed as raw JSON string in the beginning will come back the same)

soswow avatar Jul 17 '14 11:07 soswow

Something like this would work maybe?

===================================================================
--- app/components/backbone-associations/backbone-associations.js   (revision )
+++ app/components/backbone-associations/backbone-associations.js   (revision )
@@ -260,21 +259,22 @@
                 // if `relations` are available.
                 _.each(this.relations, function (relation) {
                     var relationKey = relation.key,
+                        referenceKey = relation.referenceKey || relation.key,
                         activationContext = relation.scope || root,
                         relatedModel = this._transformRelatedModel(relation, attributes),
                         collectionType = this._transformCollectionType(relation, relatedModel, attributes),
                         map = _.isString(relation.map) ? map2Scope(relation.map, activationContext) : relation.map,
-                        currVal = this.attributes[relationKey],
+                        currVal = this.attributes[referenceKey],
                         idKey = currVal && currVal.idAttribute,
                         val, relationOptions, data, relationValue, newCtx = false;

                     // Merge in `options` specific to this relation.
                     relationOptions = relation.options ? _.extend({}, relation.options, options) : options;

-                    if (attributes[relationKey]) {
+                    if (attributes[referenceKey]) {

                         // Get value of attribute with relation key in `val`.
-                        val = _.result(attributes, relationKey);
+                        val = _.result(attributes, referenceKey);

                         // Map `val` if a transformation function is provided.
                         val = map ? map.call(this, val, collectionType ? collectionType : relatedModel) : val;

And now my relation definition looks like

relations: [{
    type: Backbone.One
    relatedModel: 'Project'
    key: 'project'
    referenceKey: 'pid'
    isTransient: true
    map: (id) ->
      toggl.model.Projects.get id
  }]

In result. I get project on .get('project') I get id on .get('pid') and this id still in the JSON response

Looks like if I set new project task.set 'pid', 'test2' it also works well.

soswow avatar Jul 17 '14 12:07 soswow

I ran into a similar situation. Without modifying Backbone Associations, you could do this more easily. In your original example you wrote this:

class Task extends Backbone.AssociatedModel
  relations: [{
    type: Backbone.One
    relatedModel: 'model.Project'
    key: 'pid'
    map: (pid) ->
      someGlobalScope.projects.get pid
  }]

You could do the following to get your desired result without any modifications to the lib.

class Task extends Backbone.AssociatedModel
  relations: [{
    type: Backbone.One
    relatedModel: 'model.Project'
    key: 'project'
    map: ->
      someGlobalScope.projects.get @get 'pid'
  }]

vinnymac avatar Dec 11 '14 03:12 vinnymac