0

I have two models (teams and students) and when creating a team I want to be able to add a student to the team using their email. I can do this in the rails console by doing team.students << student but I am unsure how to translate that functionality in the controller and view.

Team controller:

def new
    @team = Team.new
end

def add_student
  @team = Team.find(params[:id])
  @team.students << Student.find(params[:student_email])
end

def create
    @team = Team.new(team_params)
    if @team.save
      redirect_to teams_path
    else
      render 'new'
    end
end
  private
    
    def team_params
      params.require(:team).permit(:name, student_attributes:[])
    end

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

Team view:

    <%= form_with(model: @team, local: true) do |f| %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>


      <%= fields_for :student do |s|%>
        <%= s.label :email%>
        <%= s.text_field :email, class: 'form-control' %>
      <% end %>
      
      <%= f.submit "Create Team", class: "btn btn-primary" %>
    <% end %>

Thank you for your help

AlexM
  • 1

1 Answers1

0

You can do a lot better then just using HABTM:

class Team < ApplicationRecord
  has_many :memberships
  has_many :students, through: :memberships
end

class Student < ApplicationRecord
  has_many :memberships
  has_many :teams, through: :memberships
end

# rails g  model team student:belongs team:belongs_to
class Membership < ApplicationRecord
  belongs_to :student
  belongs_to :team
  validates_uniqueness_of :student_id, scope: :team_id
end

This also creates a many to many assocation but it gives you an actual model so you can access additional columns on the table (like for example if you want to add roles or keep track of who added the student to the team) and its actually a real entity in your buisness logic instead of just a peice of plumbing.

HABTM is actually quite useless.

To add/remove members from a team you create and destroy memberships.

resources :teams do
  resources :memberships, 
    only: [:new, :create, :destroy]
    shallow: true
end
class MembershipsController < ApplicationController
  before_action :set_team, only: [:new, :index, :create]

  # GET /teams/1/memberships/new
  def new
    @students = Student.where.not(id: @team.students)
    @membership = @team.memberships.new
  end

  # POST /teams/1/memberships
  def create
    @membership = @team.memberships.new(membership_params)
    if @membership.save
      redirect_to @team, notice: "Student added to team"
    else
      @students = Student.where.not(id: @team.students)
      render :new
    end
  end

  # DELETE /memberships/1
  def destroy
    @membership.destroy
    redirect_to @membership.team, notice: "Student removed from team"
  end

  private

  def set_team
    @team = Team.find(params[:team_id])
  end

  def set_membership
    @membership = Membership.find(params[:id])
  end

  def membership_params
    params.require(:membership)
          .permit(:student_id)
  end
end
<%= form_with(model: @membership, local: true) do |f| %>
  <div class="field">
    <%= f.label :student_id %>
    <%= f.collection_select :student_ids, @students, :id, :email %>
  </div>
  <%= f.submit %>
<% end %>

As a rule of thumb if you're creating a method on your controller thats not one of the standard CRUD methods and it contains a synonym to of one of them (add, remove, etc) you're almost certainly doing it wrong and should treat it as separate RESTful resource.

max
  • 96,212
  • 14
  • 104
  • 165