web-dev-qa-db-ja.com

Rails 4つのネストされた属性とhas_many:フォーム内の関連付け

フォームを使用したhas_many:throughアソシエーションの管理に問題があります。私がしたくないことは、そこに大量の情報が存在する関連モデルの属性を編集することではなく、関連付けのみを管理したいことです。私は自分のアクションに渡されるフォームパラメータを操作することでこれを行うことができ、リレーションシップを手動で構築できることを理解していますが、可能であればRailsの方法を選びます。

私の場合の興味深い点は、このhas_many:through関連付けが、accepts_nested_attributes_forを使用して既に保存しているモデル上に実際にあるということです。

私のモデルは次のとおりです。目標、マイルストーン、プログラム。

class Goal < ActiveRecord::Base
  has_many :milestones, inverse_of: :goal, dependent: :destroy
  accepts_nested_attributes_for :milestones, :allow_destroy => true
end

class Milestone < ActiveRecord::Base
  belongs_to :goal, inverse_of: :milestones

  has_many :milestone_programs
  has_many :programs, :through => :milestone_programs
end

class Program < ActiveRecord::Base
end

目標編集ビューで、マイルストーンを追加および削除できるようにする必要があります。これらのマイルストーンについては、プログラムの関連付けを追加および削除できるようにする必要があります。これは私のフォームのコードです。

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

  <%= f.fields_for :milestones do |f_milestone| %>

    <%= f.hidden_field :id, :value => f.object.id %>
    <%= f.hidden_field :name, :value => f.object.name %>
    <a href="javascript:void(0)" class="milestone-remove">- remove</a>

    <ul>
      <%= f.fields_for :programs do |f_prog| %>
        <li>
          <%= f_prog.object.name %>
          <a href="javascript:void(0)" class="program-remove">- remove</a>
        </li>
      <% end %>
    </ul>

  <% end %>

  <%= f.submit 'Save' %>

<% end %>

私のコントローラーには、

class GoalsController < ApplicationController

    # PATCH/PUT /goals/:id
    def update
      if @goal.update(goal_params)
        redirect_to @goal
      end
    end

    def goal_params
      params.require(:goal).permit(:name, :milestones_attributes => [ :id, :name, :_destroy ])
    end

end

このフォームはワークシートのようなものである必要があり、変更を加えて最後に[保存]をクリックした後にのみ変更を保存できるため、cocoonやnested_formsなどのgemが役立つとは思いません。

これまでの私のコードは、マイルストーンとその属性に関連する目標を管理するために完全に機能します。しかし、今、それらのマイルストーンに関連付けられているプログラムのリストを管理できるようにしたいと考えています。

Accepts_nested_attributes_forを使用してみましたが、モデルのネストされた属性を編集する必要がないため、プログラムの属性は静的なままであるため、それは正確には必要ではありません。

各プログラムが関連付けを作成するために、フォームに次のようなものを含めることができると考えました。

<input type="hidden" name="goal[milestones_attributes][1][program_ids][1]" >

しかし、それでも機能しません(もちろん、ホワイトリストに登録されたパラメーターに:program_idsを追加しました)。コントローラーに追加する必要がある魔法Rails=メソッドはありますか?

ここに何が欠けていますか?

前もって感謝します!

20
Cameron

has_many :through 関係を使用する場合、次のように、異なるモデルにnested_attributesを渡す必要があります。

モデル

class Goal < ActiveRecord::Base
  has_many :milestones, inverse_of: :goal, dependent: :destroy
  accepts_nested_attributes_for :milestones, :allow_destroy => true

  def self.build
      goal = self.new
      goal.milestones.build.milestone_programs.build_program
  end
end

class Milestone < ActiveRecord::Base
  belongs_to :goal, inverse_of: :milestones

  has_many :milestone_programs
  has_many :programs, through: :milestone_programs

  accepts_nested_attributes_for :milestone_programs
end

class MilestoneProgram < ActiveRecord::Base
    belongs_to :milestone
    belongs_to :program

    accepts_nested_attributes_for :program
end

class Program
    has_many :milestone_programs
    has_many :milestones, through: :milestone_programs
end

コントローラー

#app/controllers/goals_controller.rb
def new
    @goal = Goal.build
end

def create
    @goal = Goal.new(goal_params)
    @goal.save
end

private

def goal_params
    params.require(:goal).permit(milestones_attributes: [milestone_programs_attributes: [program_attributes:[]]])
end

フォーム

#app/views/goals/new.html.erb
<%= form_for @goal do |f| %>
   <%= f.fields_for :milestones do |m| %>
      <%= m.fields_for :milestone_programs do |mp| %>
          <%= mp.fields_for :program do |p| %>
               <%= p.text_field :name %>
          <% end %>
      <% end %>
   <% end %>
   <%= f.submit %>
<% end %>

これはあなたが探しているものではないかもしれないことを感謝しますが、すべての散文を読みませんでした。 nested_attributes関係を通じてhas_many :throughを渡すのに必要な情報を集めました

34
Richard Peck