Rails关联 - 强参数构建

Jam*_*ker 1 ruby database activerecord ruby-on-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
Run Code Online (Sandbox Code Playgroud)

我希望建立一个拥有玩家的团队,这些团队将由用户选择.我可以通过以下方式在控制台中完美地完成此操作.

@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 
Run Code Online (Sandbox Code Playgroud)

然而,由于强大的参数,我真的很难通过参数.这是我的看法

<%= 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 %>
Run Code Online (Sandbox Code Playgroud)

我想使用团队参数和使用播放器参数的播放器来构建团队,但是我无法弄清楚如何在控制器中使用它.

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
Run Code Online (Sandbox Code Playgroud)

欢迎任何解决方案,以及任何改进.

编辑 - 增加的模式

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
Run Code Online (Sandbox Code Playgroud)

编辑2

由于这还没有得到回答,我将添加更多关于我正在尝试的解释.

用户有一个团队,我可以建立团队,并且由于ActiveRecord也建立了关系.然后用户团队有很多玩家和玩家有很多团队,当我尝试建立这种关系时,玩家表永远不会改变,没有创建任何关系.

我觉得我应该再次强调以下功能在rails控制台中完美运行

@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 
Run Code Online (Sandbox Code Playgroud)

团队设置为接受嵌套参数,所以我认为这将工作.

@team = @user.build_team(team_params)

def team_params
    params.require(:team).permit(:name, players_attributes: [:name, :role]) 
end
Run Code Online (Sandbox Code Playgroud)

我相信这应该建立玩家模型并创建关系,但是没有玩家被插入并且没有建立任何关系.

Kir*_*rat 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
Run Code Online (Sandbox Code Playgroud)

在此之后,更新视图如下:

<%# 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 %> 
Run Code Online (Sandbox Code Playgroud)

设置一个实例变量@team,new并为此构建玩家@team.在视图代码中使用@team实例变量作为参数form_for.

我还建议在create动作中进行一些调整,以便了解团队是否得到了保存.并修复了team_params允许嵌套属性的方法players.

UPDATE

使用方法@team作为参数form_for是资源导向的风格和更喜欢的方式.阅读关于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 %> 
Run Code Online (Sandbox Code Playgroud)

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 %> 
Run Code Online (Sandbox Code Playgroud)