dhruvaray/backbone-associations

CRUD in nested(related) models

Closed this issue · 4 comments

Hi, say I have:

var Post = Backbone.AssociatedModel.extend({
    urlRoot: '/posts',
    defaults: {
        content: 'Post by user'
    }
});

var Posts = Backbone.Collection.extend({model: Post});

and a User model:

var User = Backbone.AssociatedModel.extend({
    urlRoot: '/users',
    relations: [
        {
            type: Backbone.Many,
            key: 'posts',
            collectionType: Posts.extend({
                url: function () {
                    return '/users/' + user.id + '/posts';
                }
            })
        }
    ]
});

I am trying to basically create an extra /posts CRUD route, after /users/{id}, but just overriding the url doesn't seem to work.

So basically, I want to be able to do:

var post = new Post({content: 'Blog post'});
user.get('posts').create(post); // => POST at /user/id/posts

Right now it just does a POST at /posts, which is not what I want to do.
Is this possible at all?

The reason for this is that in the backend route for /users/id/posts , I want to see the user ID so that I can associate it with this new Post instance. (just for the record, I am using a graph database(Neo4j) on the backend, so in terms of a graph database, I want to add a relationship between my User node and the to be created Post node).

Any help or suggestion would be appreciated!

Try using this one:

var User = Backbone.AssociatedModel.extend({
    urlRoot: '/users',
    initialize: function() {
       var user = this;
        this.get('posts').url = function(){
            return '/users/' + user.id + '/posts';
        }       
    },
    relations: [
        {
            type: Backbone.Many,
            key: 'posts',
            collectionType: Posts
        }
    ],
    defaults: {
        posts:  []
    }
});

For more details, you can take a look at this recipe.

Let me know if you need any help on this issue.

Thanks for responding. I did try that initially before I switched to the version of the User model in my initial post (ie extending the Posts collection to override the url), and it still does the same.

It works when I try to fetch the collection, ie if I do:

user.get('posts').fetch(); // => GET /user/10/posts 

But if I try any of the other Backbone collection methods it fails. Ex:

user.get('posts').create({content: 'test'}); 
// => it does a POST at /posts instead of /user/10/posts
user.get('posts').sync(); // => Error message saying 'url' has not been specified, when it clearly has been. 
users.get('posts').url(); // => users/10/posts

So I am not sure if this is because how Backbone works, or because of backbone-associations overriding sync and create maybe?

Here is a jsfiddle where you can try the exact code I'm running: http://jsfiddle.net/gonigkum/2TsMQ/1/

Thanks!

user.get('posts').sync() needs method and model. Backbone obtains url from model object. So I think sync will definitely fail without arguments.

In your case, Backbone uses urlRoot to save newly created Post model.

var Post = Backbone.AssociatedModel.extend({
    // urlRoot: '/posts',    // Try without urlRoot 
    defaults: {
        content: 'Post by user'
    }
});

After that change, user.get('posts').create({id: 45, content:"Hello"}) will make PUT request.

Let me know if it works for you.

Did this work for you?