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 g开发者_开发知识库reat. Thanks!
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!
[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.
精彩评论