我在工作包和任务之间创建了多对多的关系:
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}) %>
我错过了什么?
答案 0 :(得分:1)
如果您希望用户能够选择现有任务,请使用collection_select
或collection_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_many
或has_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_checkboxes
和fields_for/nested_attributes
来创建一个表单,允许用户动态选择和创建新任务。