Vieenay Siingh
Vieenay Siingh

Reputation: 867

not able to save data in join table for has_many_through associations in rails

I have user, team and team_user model. team_use is joining model as a result of has_many through association between user and team. team_user has user_id and team_id from user and team tables. team has name and team_lead_id( which would a user from user table).

Problem:

When I create new team, I want to add users and team lead to team. A team can have multiple user but only one team lead. While creating team , user_id and team_id should be saved in team_user table. But this is not happening. I tried with following code in team_controller.rb:

def create
    @team = Team.new(params[:team])
      @user_team = TeamUser.new( { user_id: '@user.id', team_id: 'params[:team_id]' } )
      if @team.save
        flash[:notice] = 'Team has been created'
        redirect_to teams_path
      else
        flash[:alert] = 'Team not created'
        redirect_to teams_path
      end
  end

With TeamUser.new( { user_id: '@user.id', team_id: 'params[:team_id]' } ) , I tried to create user and team entries in team_user table but failed.

Code is following.

teams_controlller.rb

class TeamsController < ApplicationController
  before_filter :authorize_admin!

  def index
    @teams = Team.all
    @team = Team.new
  end

  def new
    @team = Team.new
  end

  def create
    @team = Team.new(params[:team])
      @user_team = TeamUser.new( { user_id: '@user.id', team_id: 'params[:team_id]' } )
      if @team.save
        flash[:notice] = 'Team has been created'
        redirect_to teams_path
      else
        flash[:alert] = 'Team not created'
        redirect_to teams_path
      end
  end

  def show
    @team = Team.find(params[:id])
  end

  def edit
    @team = Team.find(params[:id])
  end

  def update
    @team = Team.find(params[:id])
      if @team.update_attributes(params[:team])
        flash.notice = "Team #{@team.name} has been updated"
        redirect_to team_path
      else
        render 'edit'
      end
  end

  def destroy
    @team = Team.find(params[:id])
    @team.destroy
    redirect_to action:  'index'
  end

end

team.rb

class User < ActiveRecord::Base
 has_many :roles,  through: :role_users
 has_many :role_users
 has_many :leaves
 serialize :role
 has_many :teams, through: :team_users
 before_save :make_array
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
  :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :username, :email, :password, :password_confirmation,
  :remember_me, :first_name, :last_name, :is_admin, :contact_no, :birth_date,
  :joining_date, :is_active, :role, :is_manager, :user_code, :designation
  # attr_accessible :title, :body

   def active_for_authentication?
     super && is_active?
   end

  def make_array
   self.role.reject!(&:blank?) if self.role
  end

end

team_user.rb

class TeamUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :team
  attr_accessible :team_id, :team_lead_id, :user_id
end

team.rb

class Team < ActiveRecord::Base
  has_many :users, through: :team_users
  has_many :team_users
  attr_accessible :name, :team_lead_id
end

_form.html.erb(team)

<%= form_for @team do |f| %>
  <% @team.errors.full_messages.each do |msg| %>
    <li><%= msg %></li>
  <% end %>

  <div style=" margin-top:10px">
    <label> Team Name </label>
    <%= f.text_field :name, :class => 'text_field' %>
  </div>

  <label> Add Users </label>
  <%= select_tag "TeamUser[user_id]", options_from_collection_for_select( User.all, "id", "first_name"), :style => "width:270px; height:35px", :id => "drp_Books_Ill_Illustrations",
      :class => "leader MultiSelctdropdown Books_Illustrations" %>

   <label> Team Lead </label>
  <%= f.select(:team_lead_id, User.all.map { |u| [u.first_name, u.id] }) %>

  <div class=modal-footer>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
    <%= f.submit "Create Team", :class => 'btn btn-primary' %>
  </div>
<% end %>

schema.rb

 create_table "team_users", :force => true do |t|
    t.integer  "team_id"
    t.integer  "user_id"
    t.datetime "created_at",   :null => false
    t.datetime "updated_at",   :null => false
  end

  create_table "teams", :force => true do |t|
    t.string   "name"
    t.datetime "created_at",   :null => false
    t.datetime "updated_at",   :null => false
    t.integer  "team_lead_id"
  end

I am able to create team with team_lead_id in team model but failed to save data in team_user team.

Upvotes: 1

Views: 2714

Answers (1)

Marek Lipka
Marek Lipka

Reputation: 51151

The reason it's not being saved is that you call new instead of create. Besides, when you are trying to create TeamUser record, you don't know @team.id yet it's not saved at this time. But there's also other things that need to be fixed. First, your new action should look:

def new
  @team = Team.new
  @users = User.all
end

Now you don't need to call User.all twice in your view, which isn't very good practice. Now, the view:

  <%= form_for @team do |f| %>
  <% @team.errors.full_messages.each do |msg| %>
    <li><%= msg %></li>
  <% end %>

  <div style=" margin-top:10px">
    <label> Team Name </label>
    <%= f.text_field :name, :class => 'text_field' %>
  </div>

  <label> Add Users </label>
  <%= select_tag "users[]", options_from_collection_for_select( @users, :id, :first_name), :style => "width:270px; height:35px", :id => "drp_Books_Ill_Illustrations",
      :class => "leader MultiSelctdropdown Books_Illustrations", :multiple => true %>

   <label> Team Lead </label>
  <%= select_tag(:team_lead_id, options_from_collection_for_select(@users, :id, :first_name)) %>

  <div class=modal-footer>
    <button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
    <%= f.submit "Create Team", :class => 'btn btn-primary' %>
  </div>
<% end %>

In your create action:

def create
  @team = Team.new(params[:team])
  team_lead = User.find(params[:team_lead_id])
  @team.team_lead = team_lead
  if @team.save
    users = User.where(:id => params[:users])
    users.each {|user| @team.users << user}
    flash[:notice] = 'Team has been created'
    redirect_to teams_path
  else
    flash[:alert] = 'Team not created'
    redirect_to teams_path
  end
end

And you should also update your Team model:

class Team < ActiveRecord::Base
  has_many :users, through: :team_users
  has_many :team_users
  belongs_to :team_lead, class_name: 'User'
  attr_accessible :name
end

Since you display form not only in new action, I advice you to make before_filter in your controller which would set variables needed for form:

before_filter :set_form_variables, only: [:new, :index] # everywhere the form is displayed
# ...
private
  def set_form_variables
    @team = Team.new
    @users = User.all
  end

Upvotes: 2

Related Questions