2014-02-24 50 views
20

我有一個問題管理has_many:通過關聯使用表單。我不想做的是編輯其中存在大量信息的相關模型的屬性,而我只想管理關聯。我知道我可以通過操作傳遞給我的動作的表單參數並手動建立關係來做到這一點,但如果可能的話,我寧願採用Rails的方式。關於我的情況Rails 4嵌套屬性和has_many:通過關聯形式

一個有趣的事情是,這的has_many:通過協會實際上是在這我已經使用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 

這種形式需要像一個工作表,你可以進行更改,只有當你點擊保存在最後保存更改,所以我不相信寶石如繭或nested_forms會有所幫助。

我的代碼完美地工作到目前爲止管理我的目標相關的里程碑及其屬性。但現在我希望能夠管理與這些里程碑相關的程序列表。

我嘗試過使用accept_nested_attributes_for,但那不完全是我想要的,因爲我不在乎編輯模型的嵌套屬性,程序屬性保持靜態。

我想我也許能在我的形式是這樣的每個程序建立關聯:

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

但是,這並不工作,要麼(當然我說:program_ids到我的白名單參數)。有沒有我需要添加到我的控制器的神奇導軌方法?

我在這裏錯過了什麼?

在此先感謝!

回答

34

採用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 %> 

我很欣賞這可能不是你在尋找什麼,但TBH我沒看過你所有的散文。我剛剛收集你需要幫助通過nested_attributes通過一個has_many :through關係

+0

謝謝豐富 - 編輯我的「散文」一點點,以達到點快一點。我不想管理程序屬性,我只想添加和從關聯中刪除它們。我可以像我在我的問題中所說的那樣管理nested_attributes。 – Cameron

+1

沒問題 - 希望我沒有得罪或什麼!如果你在更新時告訴我,我會改變我的答案。我認爲你正在尋找[<<](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)[這裏有很好的教程](http://techspry.com/ruby_and_rails/active- records-or-push-or-concat-method /) –

+0

修正!感謝您的鏈接 - 我會使用它,如果我最終手動在代碼中寫入代碼,我希望有一個漂亮的導軌方法,我可以在它的地方使用。 – Cameron