web-dev-qa-db-fra.com

Rails 4 attributs imbriqués et has_many: via l'association dans un formulaire

J'ai un problème pour gérer un has_many: par association à l'aide d'un formulaire. Ce que je ne veux pas faire, c'est éditer les attributs du modèle associé dont il existe une pléthore d'informations, plutôt, je veux gérer l'association SEULEMENT. Je comprends que je pourrais le faire en manipulant les paramètres de formulaire passés à mon action et en créant les relations manuellement, mais je préférerais suivre la méthode Rails, si cela est possible).

Une chose intéressante à propos de mon cas est que cette has_many: through association est en fait sur un modèle que j'enregistre déjà en utilisant accept_nested_attributes_for

Voici mes modèles: objectifs, jalons et programmes.

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

Maintenant, dans ma vue de modification d'objectif, je dois pouvoir ajouter et supprimer des jalons, et pour ces jalons, je dois pouvoir ajouter et supprimer des associations de programmes. Ceci est le code de mon formulaire.

<%= 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 %>

Dans mon contrôleur, j'ai

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

Ce formulaire doit être comme une feuille de calcul où vous pouvez apporter des modifications et enregistrer uniquement vos modifications une fois que vous avez cliqué sur enregistrer à la fin, donc je ne pense pas que des gemmes telles que cocoon ou nested_forms vous aideront.

Mon code fonctionne parfaitement jusqu'à présent pour gérer mes objectifs associés et leurs attributs. Mais maintenant, je veux pouvoir gérer la liste des programmes associés à ces jalons.

J'ai essayé d'utiliser accept_nested_attributes_for mais ce n'est pas exactement ce que je veux parce que je n'aime pas éditer les attributs imbriqués du modèle, les attributs du programme doivent rester statiques.

J'ai pensé que je pourrais avoir quelque chose comme ça dans ma forme pour chaque programme pour construire les associations:

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

Mais cela ne fonctionne pas non plus (bien sûr, j'ai ajouté: program_ids à mes paramètres en liste blanche). Existe-t-il une méthode magique Rails que je dois ajouter à mon contrôleur?

Qu'est-ce que j'oublie ici?

Merci d'avance!

20
Cameron

Lorsque vous utilisez un has_many :through relation, vous devez passer le nested_attributes à travers les différents modèles, comme ceci:

Modèles

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

Contrôleur

#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

Formulaire

#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 %>

J'apprécie que ce ne soit pas exactement ce que vous cherchez, mais je n'ai pas lu toute votre prose. Je viens de comprendre que vous aviez besoin d'aide pour passer nested_attributes à travers un has_many :through relation

34
Richard Peck