Rails 带茧的嵌套形式。使用模型的属性

Rails nested form with cocoon. Attributes using model

我正在尝试创建一个包含选项和子选项的嵌套表单,它们都来自同一个名为 Option 的模型。以下是文件的内容:

型号:

class Option < ApplicationRecord
  belongs_to :activity
  has_many :option_students
  has_many :students, through: :option_students

  has_many :suboptions,
    class_name: "Option",
    foreign_key: "option_id"

  belongs_to :parent,
    class_name: "Option",
    optional: true,
    foreign_key: "option_id"

  accepts_nested_attributes_for :suboptions,

    reject_if: ->(attrs) { attrs['name'].blank? }

  validates :name, presence: true
end

控制器:

class OptionsController < ApplicationController
  include StrongParamsHolder

  def index
    @options = Option.where(option_id: nil)
  end

  def show
    @option = Option.find(params[:id])
  end

  def new
    @option = Option.new()
    1.times { @option.suboptions.build}
  end

  def create
    @option = Option.new(option_params)
    if @option.save
      redirect_to options_path
    else
      render :new
    end
  end

  def edit
    @option = Option.find(params[:id])
  end

  def update
    @option = Option.find(params[:id])
    if @option.update_attributes(option_params)
      redirect_to options_path(@option.id)
    else
      render :edit
    end
  end

  def destroy
    @option = Option.find(params[:id])
    @option.destroy
    redirect_to options_path
  end

end

_form.html.erb:

<%= form_for @option do |f| %>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %><br>
    <%= f.label :activity %><br>
    <%= select_tag "option[activity_id]", options_for_select(activity_array) %><br>
  </p>

  <div>
    <div id="suboptions">
      <%= f.fields_for :suboptions do |suboption| %>
        <%= render 'suboption_fields', f: suboption %>
      <% end %>

      <div class="links">
        <%= link_to_add_association 'add suboption', f, :suboptions %>
      </div>
    </div>
  </div>

  <p>
    <%= f.submit "Send" %>
  </p>
<% end %>

_suboption_fields.html.erb

<div class="nested-fields">
  <%= f.label :suboption %><br>
  <%= f.text_field :name %>
  <%= link_to_remove_association "X", f %>
</div>

StrongParamsHolder:

def option_params
    params.require(:option).permit(:name, :activity_id, :students_ids => [], suboptions_attributes: [:id, :name])
end

视图已正确创建,但未保存。它在创建控制器时转到 "render :new"。我觉得应该是params的问题,但是我不确定是什么。

可能由于验证失败而未保存。如果您使用 rails 5,belongs_to 现在更严格,并且为了能够保存嵌套参数,您需要使关联之间的 connection/relation 显式。

所以恕我直言,如果您将 inverse_of 添加到您的关系中,它将起作用,如下所示:

has_many :suboptions,
  class_name: "Option",
  foreign_key: "option_id",
  inverse_of: :parent 

belongs_to :parent,
  class_name: "Option",
  optional: true,
  foreign_key: "option_id"
  inverse_of: :suboptions

如果另一个验证失败,它也可能有助于在您的表单中列出错误(例如 @option.errors.full_messages.inspect 之类的东西会有所帮助:)

顺便说一句:我会将数据库中的 option_id 字段重命名为 parent_id,因为这样可以更清楚地传达其含义。