user3787971
user3787971

Reputation: 457

Limiting a select_tag list to only objects that belong_to another object in rails

I have a list of classrooms that belong_to a school and a school has_many classrooms. I have users who belong to a school and fill out a form of information. On this form I have the following selector:

Pins/_form.html.erb

    <div class="form-group">
     <%= label_tag(:classroom, "Select your classroom:") %>
     <%= select_tag "pin[code]", options_from_collection_for_select(Classroom.all, "code", "code", ) %>
   </div>

Right now this form shows all the classrooms listed in the data base. I need it to show just the classrooms that belong to the school. Classrooms have a name and a code, the code is what ties the students form submission to that classroom.

I'm still learning RoR and have struggled to figure this type of problem one out on a couple of occasions.

Here's the Classroom Controller

class ClassroomsController < ApplicationController
  before_action :set_classroom, only: [:show, :edit, :update, :destroy]
  after_action :verify_authorized
  respond_to :html
def home
  @classrooms = Classroom.all
  respond_with(@classrooms)
  authorize @classrooms
end 
 def index
   @classrooms = Classroom.all
   respond_with(@classrooms)
   authorize @classrooms
end

def show
  respond_with(@classroom)
end

def new
  @school = School.find(params[:school_id])
  @classroom = @school.classrooms.new
  @teachers = @school.teachers
  respond_with(@classroom)
  flash[:notice] = "Classroom created."
  authorize @classroom
end

 def edit
 end

def create
  @school = School.find(params[:school_id])
  @classroom = @school.classrooms.new(classroom_params)
  @classroom.save
  respond_with @school
  authorize @classroom
end

def update
  @classroom.update(classroom_params)
  respond_with([@school,@classroom])
end

def destroy
  @classroom.destroy
  redirect_to @school
  flash[:notice] = "You have succesfully deleted the classroom."
end

  private
    def set_classroom
     @classroom = Classroom.find(params[:id])
     @school = School.find(params[:school_id])
     authorize @classroom
   end

   def classroom_params
    params.require(:classroom).permit(:teacher_id, :name, :code)
  end
end

Pins_controller

 class PinsController < ApplicationController
 before_action :set_pin, only: [:show, :edit, :update, :destroy]
  respond_to :html

def show
   respond_with(@pin)
 end

def new
  @pin = Pin.new
  @emotions = Emotion.all 
  @causes = Cause.all
  @school = School.find(params[:school])
  @classrooms = @school.classrooms
  respond_with(@pin)
  authorize @pin
 end

 def edit
 end

def create
  code = params[:pin][:code]
  @classroom = Classroom.where('code LIKE ?', code).first
  unless @classroom
    flash[:error] = "Classroom code incorrect"
    @emotions = Emotion.all 
    @causes = Cause.all
    render :new
   else
    params[:pin][:classroom_id] = @classroom.id

    @pin = Pin.new(pin_params)
    @pin.save 
      params[:pin][:cause_ids].each do |cause_id| 
      @cause = Cause.find(cause_id)
      @pin.causes << @cause
    end
     params[:pin][:emotion_ids].each do |emotion_id| 
       @emotion = Emotion.find(emotion_id)
       @pin.emotions << @emotion
     end



   if @pin.save
      redirect_to signout_path and return 
   end 
    respond_with(@pin)
    authorize @pin
   end
end


def update
  @pin.update(pin_params)
  respond_with(@pin)
  authorize @pin
end

def destroy
  @pin.destroy
  respond_with(@pin)
  authorize @pin
end

private
  def set_pin
    @pin = Pin.find(params[:id])
    authorize @pin
  end

 def pin_params
   params.require(:pin).permit(:user_id, :question, :question1, :question2, 
    :question3, :question4, :question5, :classroom_id, :sad, 
    :happy, :mad, :brave, :embarrassed, :sorry, :frustrated, 
    :silly, :left_out, :excited, :hurt, :jealous, :confused, 
     :proud, :other)
  end

end

School.rb model

 class School < ActiveRecord::Base
    has_many :users
    has_many :classrooms

    validates_uniqueness_of :code

   def classrooms
    self.users.classrooms
   end


  def students
    self.users.students
   end

  def teachers
    self.users.teachers
  end

   def admins
    self.users.admins
   end
end

User Model

class User < ActiveRecord::Base
  devise :timeoutable, :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

   has_many :pins
   has_many :reflections
   has_many :classrooms, :foreign_key => :teacher_id
   belongs_to :school

  validates :name, presence: true
  validates :role, presence: true
 # validates :school, presence: true

   scope :students, -> { where(role: "student") }
   scope :teachers, -> { where(role: "teacher")}
   scope :teachers_and_admins, -> { where(:role => ["teacher", "admin"]) }
   scope :admins, -> { where(role: "admin" ) }
   scope :online, lambda{ where("updated_at > ?", 15.days.ago) }
  def online?
    updated_at > 15.days.ago
   end

  def admin?
     role == "admin"
   end 

   def teacher?
    role == "teacher"
  end

  def student?
   role == "user" || role == "student"
  end

 def superadmin?
   role == "superadmin"
 end

 def admin_of_school?(school)
   self.admin? && self.school == school
 end

 def teacher_of_school?(school)
   self.teacher? && self.school == school
 end

def admin_or_teacher_of_school?(school)
   self.admin_of_school?(school) || self.teacher_of_school?(school)
 end

end

classroom model

class Classroom < ActiveRecord::Base

   belongs_to :school
   belongs_to :teacher, :class_name => "User"
   has_and_belongs_to_many :users

   has_many :pins
   has_many :reflections

   validates_presence_of :school
   validates_presence_of :teacher
   validates :code, :uniqueness => { :scope => :school_id }


end 

I found a similar answer here: Rails only give records that "belong_to"

But it didn't help me understand how to limit the drop down list to only Classrooms of the current school.

Any ideas on how to handle his situation?

Upvotes: 0

Views: 41

Answers (1)

Mohammad AbuShady
Mohammad AbuShady

Reputation: 42789

You already select the school in the controller, so just use it in the view, instead of doing a Classroom.all do a @school.classrooms

<%= select_tag "pin[code]", 
  options_from_collection_for_select(@school.classrooms, "code", "code")
%>


This part isn't an answer, but improvements and fixes to your code

First the User model can stay as it is

class User < ActiveRecord::Base
  #devise place holder

  # assocciations
  has_many :pins
  has_many :reflections
  has_many :classrooms, foreign_key: :teacher_id
  belongs_to :school

  #validations place holder

  #scopes
  scope :students, -> { where(role: "student") }
  scope :teachers, -> { where(role: "teacher")}
  scope :admins, -> { where(role: "admin" ) }
  scope :teachers_and_admins, -> { teachers.admins }
  scope :online, -> { where("updated_at > ?", 15.days.ago) }

  # some check methods
end

classroom class

class Classroom < ActiveRecord::Base
   belongs_to :school
   belongs_to :teacher, ->{ User.teachers }, class_name: 'User'

   has_many :pins
   has_many :reflections
end 

school class

class School < ActiveRecord::Base
  has_many :users
  has_many :admins, ->{ User.admins }, class: User
  has_many :students, ->{ User.students }, class: User
  has_many :teachers, ->{ User.teachers }, class: User
  has_many :classrooms
end

Now the controllers

You need to clean up the duplications, so here's an example from ur pins#new action

def new
  @pin = Pin.new
  prepare_lookups
  respond_with(@pin)
  authorize @pin
end

def prepare_lookups
  @emotions = Emotion.all 
  @causes = Cause.all
  @school = School.find(params[:school])
  @classrooms = @school.classrooms
end

I removed the common code to a separate method, and call it whenever i need, of course you can add or remove from that method according to your needs.

Anyways, I think you should read more about active record assocciations and the conventions in writing controller actions

Upvotes: 1

Related Questions