Rails关联 - 强参数构建

时间:2014-06-12 11:37:14

标签: ruby-on-rails ruby database activerecord

所以我必须遵循模特。

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

编辑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 

团队设置为接受嵌套参数,所以我认为这样可行。

@team = @user.build_team(team_params)

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

我认为这应该建立玩家模型并创建关系,但是没有玩家插入任何玩家并且没有建立任何关系。

4 个答案:

答案 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 %> 

@team操作中设置实例变量new,并为@team构建播放器。 在视图代码中使用@team实例变量作为form_for的参数。

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

<强>更新

使用@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 :(得分: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

即。你将不得不过滤出玩家参数

答案 2 :(得分:1)

由于我本周末经历了同样的问题,我强烈建议使用nested_form。

您可以在此处找到所有实施细节:https://github.com/ryanb/nested_form

答案 3 :(得分:0)

你与自己拥有的非常接近。

只需将:id字段添加到player_attributes params,它就适合您。

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

您可能需要查看cocoon gem。它使得处理嵌套表单和允许用户使用JS添加/删除更容易。

如果您还想允许销毁,请在播放器上添加字段_destroy的复选框,并在players_attributes中添加该键。同时更新模型以允许销毁。

型号:

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 %>
相关问题