Stack Exchange
Add additional parameters from form into join table - has_many through
My problem is that I have a has_many through relationship between workers and trainings and a special join table that contains additional information about  trainings each worker has done.
training.rb
class Training < ApplicationRecord
  belongs_to :worker, optional: true
  belongs_to :line, optional: true
  has_many :trainingslines
  has_many :trainingsmemberships
  has_many :workers, through: :trainingsmemberships
  has_many :lines, through: :trainingslines
  accepts_nested_attributes_for :worker
  accepts_nested_attributes_for :line
  accepts_nested_attributes_for :trainingsmemberships
  validates :name, presence: true, length: { minimum: 1 }
  mount_uploader :attachement
end

trainings_controller.rb
class TrainingsController < ApplicationController
  before_action :logged_in_user
  before_action :admin_user
  helper_method :sort_column, :sort_direction

  def index
    @trainings = Training.paginate(page: params[:page])
  end

  def new
    @training = Training.new
  end

  def show
    @training = Training.find(params[:id])
  end

  def insert_line(line)
    self.line.clear
    self.line << line
  end

  def insert_worker(worker)
    self.worker.clear
    self.worker << worker
  end

  def create
    @training = Training.new(training_params)
    if @training.save
      flash[:info] = "Training hinzugefügt."
      redirect_to root_url
    else
      render 'new'
    end
  end

  def destroy
    Training.find(params[:id]).destroy
    flash[:success] = "Training gelöscht"
    redirect_to trainings_url
  end

  def edit
    @training = Training.find(params[:id])
  end

  def update
    @training = Training.find(params[:id])
    if @training.update_attributes(training_params)
      flash[:success] = "Training updated"
      redirect_to training_path
    else
      render 'edit'
    end
  end

  def add_worker_tm
    @workers = Worker.order("#{sort_column} #{sort_direction}")
  end

  def addworkers_tm
    @trainings = Training.all
    @training = params[:training_id]
    @tm = Trainingsmembership.new(tm_params)
    if @tm.save
      flash[:success] = "Mitarbeiter eingefügt"
      redirect_to training_path
    else
      render 'add_worker_tm'
    end
  end


  private
  def sortable_columns
    ["name", "line_ids", "shift_id", "position_id"]
  end

  def sort_column
    Worker.column_names.include?(params[:sort]) ? params[:sort] : "name"
  end

  def sort_direction
    %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
  end

  def training_params
    params.require(:training).permit(:name, :shortname, :clockcycle, :cc, :jcms, :status, :line_ids => [], :worker_ids => [])
  end

  def tm_params
    params.require(:trainingsmembership).permit(:worker_id, :training_id, :duration, :date, :attachement, :comment)
  end

  # Confirms a logged-in user.
  def logged_in_user
    unless logged_in?
      store_location
      flash[:danger] = "Please log in."
      redirect_to login_url
    end
  end

  # Confirms an admin user.
  def admin_user
    redirect_to(root_url) unless current_user.admin?
  end
end

add_worker_tm.html.erb
<%= simple_form_for(@trainingmem = Trainingsmembership.new, url: add_worker_tm_trainings_path) do |f| %>
      <%= f.input :training_id, collection: Training.all, label_method: :name, value_method: :id, label: 'Training:', include_blank: false %>
      <%= f.input :duration, label: 'Gültigkeit:' %>
      <%= f.input :date, :as => :string, :input_html => { data: {behaviour: "datepicker"}}, label: 'Prüfungstermin:' %>
      <%= f.input :worker_id, collection: Worker.all, label_method: :name, value_method: :id, label: 'Mitarbeiter', include_blank: false, input_html: { multiple: true } %>
      <%= f.input :comment, as: :text, label: 'Bemerkung:' %>
      <%= f.input :attachement, as: :file, label: 'PDF-Protokoll:' %>
      <%= f.submit "Edit training", class: "btn btn-primary" %>
    <% end %>

trainingsmembership.rb
class Trainingsmembership < ApplicationRecord
      belongs_to :worker
      belongs_to :training
    end

My trainingsmembership table has the following columns:
create_table :trainingsmemberships, id: false do |t|
      t.belongs_to :worker, index: true
      t.belongs_to :training, index: true
      t.integer :duration
      t.timestamp :date
      t.string :attachement
      t.text :comment
      t.timestamps
    end

I want to use my trainings controller with this simple_form to add a worker with all additional parameters into the trainingsmembership table.
I am quite new to ruby on rails and made some others has many through relationships working but all of them are without any additional data in the join tables. Or do I need a new trainingsmembership controller?
I have already googled but I have not found any usefull advice.
Edit: Currently I am getting no error message, I just will be redirected to the same page. My debuger gives this information:
Started POST "/trainings/add_worker_tm" for 127.0.0.1 at 2018-08-20 15:32:44 +0200
Processing by TrainingsController#addworkers_tm as HTML
  Parameters: {"utf8"=>"âœ“", "authenticity_token"=>"bZ0MxPcFgUe0EvoVd6VSeUWm4ZT5y0fc8A+eYZmuvvCLh8VoV0e63pxKTKSmvkUuNx1WUMB70Av6uahnt6Av/A==", "trainingsmembership"=>{"training_id"=>"1", "duration"=>"12", "date"=>"21.08.2018", "worker_id"=>["", "1"], "comment"=>""}, "commit"=>"Training bearbeiten"}
  User Load (1.0ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  â†³ app/helpers/sessions_helper.rb:22
Unpermitted parameter: :worker_id
   (1.0ms)  BEGIN
  â†³ app/controllers/trainings_controller.rb:72
  Training Load (0.0ms)  SELECT  `trainings`.* FROM `trainings` WHERE `trainings`.`id` = 1 LIMIT 1
  â†³ app/controllers/trainings_controller.rb:72
   (0.0ms)  ROLLBACK
  â†³ app/controllers/trainings_controller.rb:72
  Rendering trainings/add_worker_tm.html.erb within layouts/application
  Training Load (1.0ms)  SELECT `trainings`.* FROM `trainings`
  â†³ app/views/trainings/add_worker_tm.html.erb:54
  Worker Load (1.0ms)  SELECT `workers`.* FROM `workers`
  â†³ app/views/trainings/add_worker_tm.html.erb:57
  Rendered trainings/add_worker_tm.html.erb within layouts/application (429.9ms)
  Rendered layouts/_shim.html.erb (0.0ms)
  Rendered layouts/_header.html.erb (2.0ms)
  Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 654ms (Views: 594.1ms | ActiveRecord: 3.9ms)


you want to add worker with associated training membership ?

Yes, I want to add worker with an associated training membership with other additional parameters. Just adding a worker to a training in the membership table is no problem. But with more parameters it does not work.

what error did you got while adding adding a worker to a training in the membership table with more parameters.

I dont get any error at all at the moment @PardeepSaini
This is an example what happens when I try to add a worker: it does not save and gets me back to the form, my debug query shortened: `trainingsmembership: !ruby/object:ActionController::Parameters
parameters: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
training_id: '1'
duration: '12'
date: 21.08.2018
worker_id: &1

''
'1'
comment: ''
permitted: false
converted_arrays: !ruby/object:Set
hash:
*1: true
commit: Training bearbeiten
controller: trainings
action: addworkers_tm
permitted: false`


can you post the full error stack(start to end) . I want to check how you received the params at addworkers_tm method.

It is pasted. The rollback in the log points to if @tm.save what makes sense for me that I get redirected back

I setup the application with behavior like yours and found that while setting up strong params in tm_params method you need to pass array for worker_id but you you pass it as integer value. This is because you allow multiple: true for worker field. So while you trying to save your record it just remove the worked id from you params but worker_id is a required field so it rolled back the transaction . So you need to change the tm_params method from 
def tm_params
    params.require(:trainingsmembership).permit(:worker_id, :training_id, :duration, :date, :attachement, :comment)
end

to 
def tm_params
    params.require(:trainingsmembership).permit(:training_id, :duration, :date, :attachement, :comment, worker_id: [])
end

Edit:
To make it multiple you can use another paramter and iterate over it like here:
def addworkers_tm
    @workerall = Worker.all
    @ws = params[:worker_ids].require('worker_ids')
    @trainings = Training.all
    @ws.each do |w|
      @tm = Trainingsmembership.new(tm_params)
      @tm.worker_id = w
      if @tm.save
        flash[:success] = "Mitarbeiter eingefügt"
      else
        @tm.errors
        render 'add_worker_tm'
        return
      end
    end
    redirect_to root_path
end

<% @workerall.each do |worker| %>
          <tr>
            <td><%= check_box_tag "worker_ids[worker_ids][]", worker.id %></td>
            <td><%= worker.name %></td>


That is true, it is a field because I want to add a training to multiple workers at the same time, thx :) But it does not solve the problem. I am still unable to add a worker to a training (I still get a rollback at the same line). And I see that I have no attachement value in my POST parameters. We are still missing something.

ok, put debugger after if @tm.save and then check for error @tm.errors, and see what error is causes the form to submit.

https://www.photobox.co.uk/my/photo?album_id=5393157328&photo_id=500986128033 I never debugged rails. I am writing code in rails for less than 2 weeks, hope that helps you

i don't have access to this photo. can you share your minimal app to github .

one more thing, can you try removing multiple: true and try. I think array of ids is not supported by your relationships.

Well I need to make it somehow working with multiple worker_ids. For now I made multiple: false and it works but the attachment I wanted to upload is not saved. Furthermore how to make it for multiple workers?

To make it work with multiple worker_ids you need and new table which store the worker ids and training membership id. For example you need a training_membership_workers table .

According to your edit it will create new row for each worker in training_membership table.

It will create a row for every selected worker as I wanted. Thank you for your help, with this code it is working perfectly :)
