2014-06-12 64 views
1

所以我必須遵循以下模型。Rails協會 - 強大的參數構建

class Team < ActiveRecord::Base 
    belongs_to :user 
    has_and_belongs_to_many :players 
    accepts_nested_attributes_for :players 
end 


class Player < ActiveRecord::Base 
    has_many :statistics 
    has_and_belongs_to_many :teams 
end 

我希望建立一個有玩家的團隊,這些將由用戶選擇。我可以在控制檯中完成以下操作。

@user = User.find(10) 
@team = @user.build_team(name: "MyTeam", points: 0) 
    #<Team team_id: nil, name: "MyTeam", points: 0, user_id: 10> 
@team.players.build(name: "Messi") 
    #<Player player_id: nil, name: "Messi", role: nil> 
@team.save 

但是,我真的很掙扎傳遞參數,由於強參數。這是我的看法

<%= form_for :team do |f| %> 

    <%= f.label :name %><br /> 
    <%= f.text_field :name %> 

    <%= f.fields_for :players do |players| %> 
     <%= players.label :player_name %> 
     <%= players.text_field :name %> 
    <% end %> 

    <div><%= f.submit "Create Team" %></div> 

<% end %> 

我想建立使用球隊的參數和使用播放器參數的球員的球隊,但我無法弄清楚如何在控制器得到這個工作。

class TeamController < ApplicationController 

    before_action :authenticate_user! 

    def new 
    end 

    def create 
     @user = User.find(current_user.id) 
     @team = @user.build_team(team_params) #Just the team paramaters 

     @team = @team.players.build(player_params)# I want just the player params 

     @team.save 
    end 

private 

    # I can add the player param as nested i.e. .permit(:name, :players => [:name]) 
    # but then build_team complains about receiving an array. 

    def team_params 
     params.require(:team).permit(:name) 
    end 

end 

任何解決方案,歡迎,以及任何改進。

編輯 - 添加的架構

create_table "players", primary_key: "player_id", force: true do |t| 
    t.string "name", limit: 50, null: false 
    t.string "role", limit: 30, null: false 
end 

create_table "players_teams", id: false, force: true do |t| 
    t.integer "player_id", null: false 
    t.integer "team_id", null: false 
end 

# players_teams is a Composite Primary Key, as instructed in the guides; 
# also essential for targeting. 

create_table "teams", primary_key: "team_id", force: true do |t| 
    t.string "name", limit: 200,    null: false 
    t.integer "points",    default: 0, null: false 
    t.integer "user_id",       null: false 
end 

EDIT 2

由於這還沒有得到答覆,我會添加更多的解釋,我所嘗試。

用戶有一個團隊,我可以建立團隊,並且由於ActiveRecord的關係也建立了。用戶團隊有很多玩家,玩家有很多團隊,當我嘗試建立這種關係時,玩家表永遠不會改變,不會創建任何關係。

我覺得我應該再次強調的是,以下的作品完美地在鐵軌控制檯

@user = User.find(10) 
@team = @user.build_team(name: "MyTeam", points: 0) 
    #<Team team_id: nil, name: "MyTeam", points: 0, user_id: 10> 
@team.players.build(name: "Messi") 
    #<Player player_id: nil, name: "Messi", role: nil> 
@team.save 

隊被設置爲接受嵌套的參數,所以我認爲這是可行的。

@team = @user.build_team(team_params) 

def team_params 
    params.require(:team).permit(:name, players_attributes: [:name, :role]) 
end 

我相信這應該建立玩家模型並創建關係,但是沒有玩家插入並且沒有建立關係。

+0

你能從'schema.rb'分享teams'和'players'表的'模式。將其添加到問題中。 –

+0

我已經添加了。 –

回答

7

首先要在TeamsController如下幾個變化:

class TeamController < ApplicationController 

    before_action :authenticate_user! 

    def new 
     ## Set "@team" and build "players" 
     @team = current_user.build_team 
     @team.players.build 
    end 

    def create 
     @team = current_user.build_team(team_params) 
     if @team.save 
     ## Redirect to teams show page 
     redirect_to @team, notice: 'Team was successfully created.' 
     else 
     ## In case of any error while saving the record, renders the new page again 
     render action: 'new' 
     end 
    end 

    private 

    # I can add the player param as nested i.e. .permit(:name, :players => [:name]) 
    # but then build_team complains about receiving an array. 

    def team_params 
     ## Permit players_attributes 
     params.require(:team).permit(:name, players_attributes: [:id, :name]) 
    end 

end 

在此之後,更新如下觀點:

<%# Changed "form_for :team" to "form_for @team" %> 
<%= form_for @team do |f| %> 

    <%= f.label :name %><br /> 
    <%= f.text_field :name %> 

    <%= f.fields_for :players do |player| %> <%# Changed "|players|" to "|player|" %> 
     <%= player.label :name %> <%# Changed "player_name" to "name" and "players" to "player" %> 
     <%= player.text_field :name %> <%# Changed "players" to "player" %> 
    <% end %> 

    <div><%= f.submit "Create Team" %></div> 

<% end %> 

new行動中設置實例變量@team,併爲該@team構建播放器。 在您的視圖代碼中使用@team實例變量作爲form_for的參數。

我還建議在create行動中進行一些調整,以便知道團隊是否已保存。 並修復team_params方法以允許嵌套屬性players

UPDATE

使用@team作爲參數form_for方法是面向資源的風格和多首選方式。 閱讀關於usage of form_for的相當不錯的描述以獲得更好的主意。 您仍然可以在使用:team時執行所需的代碼,但其不是首選的方式

示例使用:team

<%= form_for :team do |f| %> 

    <%# ... %> 

    <%= f.fields_for :players, f.object.players.build do |player| %> <%# build the players for the team %> 
     <%# ... %> 
    <% end %> 

    <%# ... f.submit "Create Team" %> 

<% end %> 

fields_for你的情況會遍歷球員(@team.players)屬於一個特定的團隊(@team)。如果沒有玩家,那麼你不會在表單中看到任何玩家的領域,這就是爲什麼你建立玩家,所以你至少得到一些空白的領域供玩家輸入,這就是爲什麼當使用accepts_nested_attributes_for時,你需要建立嵌套屬性。您可以在控制器級別(如上面的建議代碼所示)或在表單內構建它們。

爲「表單中的」

例子:

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

    <%# ... %> 

    <%= f.fields_for :players, @team.players.build do |player| %> <%# build the players for @team %> 
     <%# ... %> 
    <% end %> 

    <%# ... f.submit "Create Team" %> 

<% end %> 
+1

這不會將球員加入球隊。 –

+0

讓我們在聊天上進行調試http://chat.stackoverflow.com/rooms/48530/ror –

+0

@JamesParker讓我知道您面臨的是什麼問題。在進行建議的更改後,您可以共享表單提交時生成的服務器日誌嗎? –

1

通常,您只需一次執行嵌套屬性,這將通過嵌套屬性創建播放器,例如,

def create 
    @user = User.find(current_user.id) 
    @team = @user.build_team(team_params) 
    @team.save 
end 

def team_params 
    params.require(:team).permit(:name, :players => [:name]) 
end 

如果你絕望到它們分開,你應該能夠做到像

def player_params 
    params.require(:team).permit(:name, :players => [:name])[:team][:players] 
end 

即你將不得不過濾掉是球員參數

+0

使用第一種方法會產生有關它接收數組的錯誤。我也不認爲這會建立球隊和球員之間的聯繫。第二個解決方案抱怨「未定義的方法'[]'爲零:NilClass」 –

+0

它會像第二個,這是什麼'params.require(:team).permit(:name,:players_attributes => [:name ])'包含? –

+0

'{「utf8」=>「✓」, 「team」=> {「name」=>「MyTeam」, 「players」=> {「name」=>「Messi」}}, 「commit」 =>「創建團隊」}'新的方法實際上錯誤說'未知的屬性:球員 ' –

0

您是非常接近與你有什麼。

只需將:id字段添加到player_attributes參數中,它應該適合您。

def team_params 
    params.require(:team).permit(:name, players_attributes: [:id, :name, :role]) 
end 

您可能想要查看cocoon寶石。它使處理嵌套表單更容易,並允許用戶使用JS添加/刪除。

如果您還想允許銷燬,請在播放器上爲字段_destroy添加複選框,然後在播放器屬性中添加密鑰。同時更新模型以允許銷燬。

型號:

class Team < ActiveRecord::Base 
    belongs_to :user 
    has_and_belongs_to_many :players 
    accepts_nested_attributes_for :players, allow_destroy: true 
end 

控制器:

def team_params 
    params.require(:team).permit(:name, players_attributes: [:id, :_destroy, :name, :role]) 
end 

查看:

<%= f.fields_for :players do |player| %> <%# Changed "|players|" to "|player|" %> 
    <%= player.label :name %> <%# Changed "player_name" to "name" and "players" to "player" %> 
    <%= player.text_field :name %> <%# Changed "players" to "player" %> 
    <%= player.check_box :_destroy %> Delete 
<% end %> 
+0

不幸的是,這是行不通的。玩家永遠不會被插入到數據庫中,因此關係永遠不會被創建。 –