Search code examples
ruby-on-railsrubydatabaseclassscaffold

How to make model associations between two models in Ruby. Or How to take data from one model and use it in another model?


So I create two models, one "Course" and one "Section" with scaffold and need Section to display Courses in a drop down menu that reflects any courses that were created in the course model and use it to create in section. I've been able to get the drop down menu displaying the courses created from "Course", but when I create the new section is displays the course as blank. Course has a Name, Department, Number, and Credit Hours. Section has Semester, Number, Course, and Room Number.

What I modified to make the drop down menu was ( in _form.html.erb of views of section )

  <div class="field">
  <%= form.label "Courses", class: 'courses'%>
  <%= form.collection_select(:section, Course.all, :id, :name) %>
  </div>

This gives an error of "Courses must exist"

Previously I had:

  <div class="field">
  <%= form.label "Courses", class: 'courses'%>
  <%= form.collection_select(:course_ids, Course.all, :id, :name) %>

This did not give an error and allowed me to create a section, just without adding the selected course to the section database.

https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select

From reading it appears :name should be defined in the models portion of Course, but when I try it gives an error. I also realize I do not have it set to record Course to a specific section ID which is why it isn't saving it when a new section is created. My question is, what do I add or modify to make that work? Is using collection select the wrong thing to do?

EDIT to include sections_controller.rb

class SectionsController < ApplicationController
  before_action :set_section, only: [:show, :edit, :update, :destroy]

  # GET /sections
  # GET /sections.json
  def index
    @sections = Section.all
  end

  # GET /sections/1
  # GET /sections/1.json
  def show
  end

  # GET /sections/new
  def new
    @section = Section.new
  end

  # GET /sections/1/edit
  def edit
  end

  # POST /sections
  # POST /sections.json
  def create
    @section = Section.new(section_params)

    respond_to do |format|
      if @section.save
        format.html { redirect_to @section, notice: 'Section was successfully created.' }
        format.json { render :show, status: :created, location: @section }
      else
        format.html { render :new }
        format.json { render json: @section.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /sections/1
  # PATCH/PUT /sections/1.json
  def update
    respond_to do |format|
      if @section.update(section_params)
        format.html { redirect_to @section, notice: 'Section was successfully updated.' }
        format.json { render :show, status: :ok, location: @section }
      else
        format.html { render :edit }
        format.json { render json: @section.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /sections/1
  # DELETE /sections/1.json
  def destroy
    @section.destroy
    respond_to do |format|
      format.html { redirect_to sections_url, notice: 'Section was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_section
      @section = Section.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def section_params
      params.require(:section).permit(:semester, :number, :course, :room_number)
    end
end

I believe I need to relate them somehow with the last part:

 def section_params
          params.require(:section).permit(:semester, :number, :course, :room_number)

EDIT:
(source: rubyisonrails.com)
http://rubyisonrails.com/pictures/part2.PNG">


Solution

  • First, you should change courses to course in the Section model. The association name for the belongs_to should always be singular

    class Section < ApplicationRecord
      belongs_to :course #singular name
    end
    

    Second, you should have course_id column instead of course in the sections table. You can generate a migration which will reflect these changes in the table

    rails g migration modify_sections
    

    The above command should generate a file like xxxxxxxmodify_sections.rb under db/migrate folder. Open the file and add

    def change
      add_column :sections, :course_id, :integer
      remove_column :sections, :course, :integer
    end
    

    and do rake db:migrate

    Now change the collection_select like the below

    <%= form.collection_select(:course_id, Course.all, :id, :name) %>
    

    And in the sections_controller#create, add

    @section.course_id = params[:section][:course_id]
    

    before the respond_to do |format|

    Finally, change course to course_id in the section_params method.

    def section_params
      params.require(:section).permit(:semester, :number, :course_id, :room_number)
    end
    

    Note:

    As you are very new to the technology, I recommend you to follow the Guides to learn.