【问题标题】:Rails Associations - Strong Parameters BuildRails 关联 - 强大的参数构建
【发布时间】:2014-06-12 11:37:14
【问题描述】:

所以我必须遵循模型。

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

我相信这应该建立玩家模型并建立关系,但是从来没有插入过任何玩家,也没有建立关系。

【问题讨论】:

  • 你能分享来自schema.rbteamsplayers 表的架构吗?将其添加到问题中。

标签: ruby-on-rails ruby database activerecord


【解决方案1】:

首先在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 的嵌套属性。

更新

使用@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) 的球员 (@team.players)。如果没有玩家,那么您将不会在表单中看到任何玩家字段,这就是您构建玩家的原因,因此您至少可以获得一些空白字段供玩家输入,这就是为什么在使用 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 %> 

【讨论】:

  • 这不会将玩家添加到团队中。
  • @JamesParker 让我知道您面临的问题。在进行建议的更改后,您能否分享提交表单时生成的服务器日志。
  • 为什么要投反对票?至少说明一个反对投票的理由。
  • 我不能投票,没有足够的积分。所以不是我 :) 我会读一下,如果我仍然遇到困难,我会很感激聊天。
  • @JamesParker 请阅读我回答中的更新部分。我添加了一些额外的点来澄清你的担忧。
【解决方案2】:

通常您只需立即执行嵌套属性,这将通过嵌套属性创建玩家,例如

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

即您将不得不只过滤掉玩家参数

【讨论】:

  • 使用第一种方法会产生关于它接收数组的错误。我也不认为这会建立球队和球员之间的联系。第二种解决方案抱怨“nil:NilClass 的未定义方法 `[]'”
  • 会是第二个,params.require(:team).permit(:name, :players_attributes =&gt; [:name]) 包含什么?
  • {"utf8"=&gt;"✓", "team"=&gt;{"name"=&gt;"MyTeam", "players"=&gt;{"name"=&gt;"Messi"}}, "commit"=&gt;"Create Team"} 新方法实际上是错误的说unknown attribute: players
  • 好吧,让它成为 player 而不是 player_attributes
  • 已经是玩家而不是players_attributes。该错误表示属性 player 未知,而不是 player_attribute。读起来很困惑;)
【解决方案3】:

由于这个周末我确实遇到了同样的问题,我强烈建议使用nested_form。

你可以在这里找到所有的实现细节:https://github.com/ryanb/nested_form

【讨论】:

    【解决方案4】:

    你与你所拥有的非常接近。

    只需将 :id 字段添加到 player_attributes 参数,它应该适合你。

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

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

    如果您还想允许销毁,请为播放器上的字段 _destroy 添加一个复选框,并在 player_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 %>
    

    【讨论】:

    • 不幸的是,这不起作用。玩家永远不会插入数据库,因此永远不会建立关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    • 1970-01-01
    • 1970-01-01
    • 2013-04-11
    相关资源
    最近更新 更多