在Rails 3中将实体添加到另一个实体(n:m关系)的RESTful方法?

时间:2011-06-15 17:20:21

标签: ruby-on-rails-3 rest has-and-belongs-to-many

这可能是一个非常简单的问题,有一个简单的解决方案,但我还没有得到它。

假设我有以下两种模式:

class Task
   has_and_belongs_to_many :users
end

class User
   has_and_belongs_to_many :tasks
end

进一步假设有一个名为“wayne”的用户,其id = 1,一个名为“go shopping”的任务,其id = 2。通过REST接口创建它们之间关系的RESTful方法是什么?

添加像users / 1 / add_task / 2这样的路由更像是RPC-Style而不是非常RESTful。那你怎么做呢?

3 个答案:

答案 0 :(得分:2)

你可以选择

用户/ {用户ID} /任务/

其中到该位置的PUT将{taskid}的任务添加到该集合;你也可以(同时)拥有

任务/ {的taskid} /用户/

其中到该位置的PUT将具有{userid}的用户添加到该集合。

答案 1 :(得分:2)

安静的方法是添加一个用作连接的模型。然后,连接表将获得自己的资源。 可以调用连接模型,例如订阅,或者,如果您没有找到合适的名称,只需将其称为TaskUser。 这部分导轨指南是相关的: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

class Task
  has_many :users, :through => "TaskUser"
end

class User
  has_many :tasks, :through => "TaskUser"
end

class TaskUser
  has_many :users
  has_many :tasks
end

在您宣布路线后提交POST to /taskusers user_idtask_id时(例如,在routes.rb中添加resource TaskUser),您可以创建一个链接这两个对象。控制器应包含以下内容:

class TaskUsersController
  def create
    TaskUser.create(:task_id => params[:task_id], :user_id => params[:user_id])
  end
end
然后,您只需拨打

即可访问用户的任务
user.tasks

就个人而言,我认为有些情况下你不需要这个解决方案,因为它会增加额外模型的所有开销。

例如,我有一个Item模型,想要为它添加标签。它是has_and_belongs_to_many在每个模型中声明的多对多关联,但我总是在项目中添加标签,而不是相反,所以前一个解决方案似乎有点多。然后我添加

put "/Items/#{item_id}/tags/#{tag_id}" => "items#add_tag"

到routes.rb。 ItemController:

class ItemController
  def add_tag
    item = Item.find_by_id(:item_id)
    tag = Tag.find_by_id(:tag_id)
    item.tags << tag
    item.save
  end
end

希望以任何方式帮助,即使我只是看到答案是迟了一年。

答案 2 :(得分:0)

你对这个解决方案有什么看法?

编辑:使用AssignmentSheet替换用户和使用Assignment任务替换此示例以适合原始问题。

一些路线:

# routes.rb
put "assignment_usage/assignment_sheet/:assignment_sheet_id/assignment/:assignment_id" => "assignment_usage#create"
  delete "assignment_usage/assignment_sheet/:assignment_sheet_id/assignment/:assignment_id" => "assignment_usage#destroy"

控制器代码:

# app/controllers/assignment_usage_controller.rb
class AssignmentUsageController < ApplicationController
  before_filter :load_instances

  # PUT /assignment_usage/assignment_sheet/:assignment_sheet_id/assignment/:assignment_id
  def create
    @assignment.assignment_sheets << @assignment_sheet
    redirect_to(@assignment_sheet, :notice => "Assignment has been successfully added.")
  end

  # DELETE /assignment_usage/assignment_sheet/:assignment_sheet_id/assignment/:assignment_id
  def destroy
    @assignment.assignment_sheets.delete @assignment_sheet
    redirect_to(@assignment_sheet, :notice => "Assignment has been successfully removed.")
  end

  private
  def load_instances
    @assignment_sheet = AssignmentSheet.find(params[:assignment_sheet_id])
    @assignment       = Assignment.find(params[:assignment_id])
  end
end

使用单独的资源可能会更清楚一点。