Rails Many_To_Many选择形式

时间:2016-12-11 19:40:15

标签: ruby-on-rails many-to-many

我在工作包和任务之间创建了多对多的关系:

class WorkPackage < ActiveRecord::Base
  has_and_belongs_to_many :tasks
end

class Task < ActiveRecord::Base
  has_and_belongs_to_many :work_packages
end

def change
    create_table :tasks_work_packages, id: false do |t|
      t.belongs_to :work_package, index: true
      t.belongs_to :task, index: true
    end
end

如果我将任务分配给工作包,它就可以了。但是现在我希望用户将任务添加到工作包中,我需要添加到控制器中,尤其是要实现该功能的表单?

我目前的解决方案不起作用:

work_package_controller:

def work_package_params
      params.require(:work_package).permit(:name, :price, tasks_attributes: [:id, :work_package_id, :task_id])
end

work_packages_form(目前为止有3种不同的选项):

    <% 3.times do %>
        <%= f.fields_for @work_package.tasks.build do |task_fields| %>
            <%= task_fields.collection_select(:id, Task.all, :id, :name, {:include_blank => true }) %>

        <% end %>
    <% end %>

    <% @work_package.tasks.each do |task| %>
        <%= f.fields_for :tasks, task do |task_fields| %>
          <%= task_fields.collection_select(:id, Task.all, :id, :name, {:include_blank => true }) %>
            <% end %>
        <% end %>

    <%= select_tag("work_package[task_ids][]", options_for_select(Task.all.collect { |task| [task.name, task.id] }, @work_package.tasks.collect { |task| task.id}), {:multiple=>true, :size=>5}) %>

我错过了什么?

1 个答案:

答案 0 :(得分:1)

选择关联记录

如果您希望用户能够选择现有任务,请使用collection_selectcollection_checkboxes

请注意,这与嵌套属性无关!不要混淆两者。

<%= form_for(@work_package) do |f| %>
  <div>
    <%= f.label :task_ids, "Tasks" %>
    <%= f.collection_check_boxes :task_ids, Task.all, :id, :name %>
  </div>
<% end %>

这将创建一个task_ids param,其中包含一组id。

当您使用has_manyhas_and_belongs_to_many时,ActiveRecord创建了一个特殊的 relation_name_ids 属性。在这种情况下task_ids。当您在模型上设置task_ids并调用.save时,AR会相应地在tasks_work_packages表中添加或删除行。

将task_ids参数列入白名单:

require(:work_package).permit(task_ids: [])

嵌套属性

如果希望用户能够同时创建或修改工作包和相关任务,则可以使用嵌套属性。

fields_for所做的是为模型关系创建范围输入。这意味着它遍历关联的记录并为每个记录创建输入:

<% form_for(@work_package) do |f| %>
  <%= f.fields_for(:tasks) do |f| %>
    <div class="field">
      <% f.string :name %>
    </div>
  <% end %>
<% end %>

fields_for会在params[:tasks_attributes]中为我们提供一系列哈希值。

这里的一个重要问题是,不会为新记录显示任务字段,也无法从编辑操作中添加新任务。

要解决此问题,您需要使用任务为工作包设定种子:

class WorkPackagesController < ApplicationController

  def new
    @work_package = WorkPackage.new
    seed_tasks
  end

  def edit
    seed_tasks
  end

  def create
    # ...
    if @work_package.save
      # ...
    else
      seed_tasks 
      render :new
    end
  end

  def update
    # ...
    if @work_package.save
      # ...
    else
      seed_tasks
      render :edit
    end
  end

  private 

  # ...

  def seed_tasks
    3.times { @work_package.tasks.new }
  end
end

将您要执行的嵌套属性列入白名单:

params.require(:work_package).permit(tasks_attributes: [:name])

结论

虽然这些是非常不同的工具,它们分开的东西并不是唯一的。您可以合并collection_checkboxesfields_for/nested_attributes来创建一个表单,允许用户动态选择和创建新任务。