首页 > 解决方案 > 向 has_and_belongs_to_many 关系添加多条记录

问题描述

我有一个 HABTM 关系设置如下:

class Game < ApplicationRecord
  has_and_belongs_to_many :players
end

class Player < ApplicationRecord
  validates :name, presence: true, uniqueness: true
  has_and_belongs_to_many :games
end

创建新游戏时,我希望用户能够选择现有玩家添加到游戏中。在 Games#new 视图中,我有:

<%= form_with(model: game) do |f| %>
  <h3>Select players participating in this game</h3>

  <% @players.each do |player| %>
    <div class="field">
      <%= check_box_tag :player_ids, player.id, false, { id: "player_#{player.id}"} %>
      <%= label_tag "player_#{player.id}", player.name %>
    </div>
  <% end %>


  <div class="actions">
    <%= f.submit 'Start Game' %>
  </div>
<% end %>

This displays each user with a checkbox to select, but when multiple players are selected and a game is created, only the last player is associated with the game.

在控制器中我有

  def new
    @game = Game.new
    @players = Player.all.sort_by &:name
  end

  def create
    @game = Game.new(game_params)

    respond_to do |format|
      if @game.save
        format.html { redirect_to @game }
        format.json { render :show, status: :created, location: @game }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @game.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def set_game
    @game = Game.find(params[:id])
  end

  def game_params
    params.permit(:player_ids)
  end

我知道我应该附加 player_ids 但我不确定如何。

标签: ruby-on-railsassociationsrelationshiprails-activerecord

解决方案


在表单中使用集合助手:

<%= form_with(model: game) do |f| %>
  <h3>Select players participating in this game</h3>

  <div class="field">
    <%= f.collection_check_boxes(:player_ids, @players, :id, :name) %>
  </div>

  <div class="actions">
    <%= f.submit 'Start Game' %>
  </div>
<% end %>

然后在你的强参数中,你需要允许一个标量值数组:

class PlayersController
  def new
    @game = Game.new
    # Sort the records in the DB, not in Ruby land.
    @players = Player.all.order(:name)
  end

  def create
    @game = Game.new(game_params)
    respond_to do |format|
      if @game.save
        format.html { redirect_to @game }
        format.json { render :show, status: :created, location: @game }
      else
        format.html do
           # required to render the form
           @players = Player.all.order(:name) 
           render :new, status: :unprocessable_entity 
        end
        format.json { render json: @game.errors, status: :unprocessable_entity }
      end
    end
  end

  private

  def set_game
    @game = Game.find(params[:id])
  end

  def game_params
    params.require(:game)
          .permit(player_ids: [])
  end
end

推荐阅读