Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Structuring a many-to-many relationship between models for Rails and Backbone.js

I'm trying to set up an item model and a tag model that have a many-to-many relationship (items have multiple tags and tags belong to multiple items). I'm using Rails and Backbone.js, so I need to have them store, retrieve and update models seamlessly between each other. I would also love it if I could save a new list of tags for a specific item in one go from the client.

What's the correct way to structure the models and controllers on the Rails side and the models on the Backbone side to keep the system RESTful and make it easy to share models between them? Specifically, what would the API look like on the server, and what would the JSON representation of the models be in saving and retrieving them?

I would really appreciate any advice on structure, and I don't really need any code or implementation details -- just a high level setup would be great. Thanks!

like image 407
Chetan Avatar asked Jul 11 '11 08:07

Chetan


2 Answers

Looks like you found your rails answer. Maybe I can help with the backbone side:

Backbone has 2 model constructs: The Model, and the Collection (the collection just being a list of models). There is no formal way of describing relationships with backbone (afaik), so you have to do it yourself. I think what I would do to handle this structure would be 3 collections:

ItemCollection

The item collection would hold all of your items, and each item would, in turn, have it's own TagCollection, which holds the tag models that are related to it.

ItemCollection.TagCollection

Holds references to the main TagCollection instance, but is a local list for this Item only. Since you can '.add' a model to a collection, then you can have multiple collections with the same models populating them.

TagCollection

The TagCollection holds your tags. It's the "main" list of tags that every ItemCollections TagCollection would reference.

For example: You have 3 tags in your TagCollection, and 2 items.

  • item_1.TagCollection has tag_A and tag_B
  • item_2.TagCollection has tag_A and tag_C

If, item_1 then has tag_C added to it, you would simply: item_1.TagCollection.add(tag_C) Similarly, removing: item_1.TagCollection.remove(tag_C) would remove it from item_1 collection, but not any others.

Regardless of the methods you utilize, you'll need to write some code in order to have it do mass updates / creates. Remember that backbone just passes the attribute list along as a JSON string in the body of the request when it does a sync. It doesn't care what it sends. So, so long as your controller was setup to accept a list (1 or more) on it's create method, you should be able to do this pretty simply by doing TagCollection.create([list of tags]). The difficult part would be to override the backbone sync to handle the successful creation, and turning [list of tags] into individual models for the collection.

Hope that helps!

like image 112
Craig Monson Avatar answered Oct 18 '22 14:10

Craig Monson


[In addition to Pope's answer:]

For reference, the Rails answer (from Creating multiple resources in a single RESTful POST in rails) is to use accepts_nested_attributes_for:

class Item < ActiveRecord::Base
  has_many_and_belongs_to :tags
  accepts_nested_attributes_for :tags
end

class Tag < ActiveRecord::Base
  has_many_and_belongs_to :items
end

The following assumes that you've added ActiveRecord::Base.include_root_in_json = false to one of your initializers (see here for why).

To save a list of tags for an item from Backbone, the answer (from Saving nested objects with Rails, backbone.js, and accepts_nested_attributes_for) is to override sync on the Item model:

sync: (method, model, options) ->
    data = JSON.stringify model.toJSON()
    if (method == "create" || method == "update")
        json = model.attributes
        json = _.extend json, {tags_attributes: model.tags.toJSON()}
        data = JSON.stringify json

    options.data = data
    options.contentType = 'application/json'
    Backbone.sync method, model, options

This solution might require a bit more hackery to get Rails to understand Backbone, but this is how you would start setting them up.

like image 29
Chetan Avatar answered Oct 18 '22 16:10

Chetan