Search code examples
ruby-on-railsnested-attributeshas-many-throughcollection-select

rails has_many through create multiple join records with collection_select


I'm pretty new to rails and trying to get three models to work together with a has_many through relationship, alternate class names on the join model, and a collection_select form to add records to the join model. The three models are below. I have "accepts_nested_attributes_for" for all associations.

User Model

has_many :match_opponents
has_many :home_opponents, :class_name => 'MatchOpponent', :foreign_key => 'home_opponent_id'
has_many :away_opponents, :class_name => 'MatchOpponent', :foreign_key => 'away_opponent_id'
has_many :matches, :through => :match_opponents

Match Model

has_many :match_opponents
has_many :home_opponents, :class_name => 'MatchOpponent', :foreign_key => 'home_opponent_id'
has_many :away_opponents, :class_name => 'MatchOpponent', :foreign_key => 'away_opponent_id'
has_many :users, :through => :match_opponents

Match Opponent Model

belongs_to :user, :inverse_of => :match_opponents
belongs_to :match, :inverse_of => :match_opponents

My Matches Controller for new and create:

def new
  @match = Match.new
  @match.home_opponents.build
  @match.away_opponents.build
end

def create
  @match = Match.create(match_params)
  @home_opponents = @match.home_opponents.create!(params[:match_opponents])
  @away_opponents = @match.away_opponents.create!(params[:match_opponents])
  if @match.save 
    redirect_to @match, notice: 'Match was successfully created.'
  end
end

My form:

= simple_form_for @match, html: { multipart: true } do |f|
  = simple_fields_for :home_opponents do |ff|
    = ff.collection_select :user_id, User.all, :id, :name, {}, {multiple: true}

  = simple_fields_for :away_opponents do |ff|
    = ff.collection_select :user_id, User.all, :id, :name, {}, {multiple: true}

Relevant permitted parameters for each model:

def match_params
  params.require(:match).permit(Match::MATCH_ATTRIBUTES)
end

MATCH_ATTRIBUTES = [:match_opponent_ids => [], :user_ids => [], match_opponents_attributes: MatchOpponent::MATCH_OPPONENT_ATTRIBUTES]

def user_params
  params.require(:user).permit(User::USER_ATTRIBUTES)
end

USER_ATTRIBUTES = [:match_opponent_ids => [], :match_ids => [], :match_opponents_attributes: MatchOpponent::MATCH_OPPONENT_ATTRIBUTES]

def match_opponent_params
  params.require(:match_opponent).permit(MatchOpponent::MATCH_OPPONENT_ATTRIBUTES)
end

MATCH_OPPONENT_ATTRIBUTES = [:id, :home_opponent_id, :away_opponent_id, :match_id, :user_id]

I've tried a lot of different configurations of the models but can't get multiple records on the join model (MatchOpponent) to save with both match_id and user_id recorded.

Thanks in advance for any help!


Solution

  • First you need to understand more about controller actions. You can read about them in the Rails Guides

    For example:

    def create
      @match = Match.create(match_params)
      @home_opponents = @match.home_opponents.create!(params[:match_opponents])
      @away_opponents = @match.away_opponents.create!(params[:match_opponents])
      if @match.save 
        redirect_to @match, notice: 'Match was successfully created.'
      end
    end
    

    Match.create already saves the object, which you save again with if @match.save. This should look like:

    def create
      @match = Match.new(match_params)
      if @match.save 
        #create opponents here
        redirect_to @match, notice: 'Match was successfully created.'
      end
    end