ruby-on-railsrubycollection-select

Rails collection_select not working for many to one relationship


I have 3 models in my rails project

Patient

class Patient < ActiveRecord::Base
    has_many :temperatures
    belongs_to :bloodgroup
    def fullname
        "#{firstName} #{surname}"
    end
end

Temperature

class Temperature < ActiveRecord::Base
    belongs_to :patient
end

BloodGroup

class BloodGroup < ActiveRecord::Base
    has_many :patients
end

collection_select works for adding a patient to a temperature using the below snippet

<div class="field">
    <%= f.label :patient_id %><br>
    <%= collection_select(:temperature, :patient_id, Patient.all, :id, :fullname, prompt: true) %>
</div>

however the below snippet - to add BloodGroup to a patient doesnt work

<div class="field">
    <%= f.label :bloodgroup_id %><br>
    <%= collection_select(:Patient, :bloodgroup_id, BloodGroup.all, :id, :id, prompt: true) %>
</div>

I'm not sure if I've not set up the collection_select properly or if I've not set up the association properly as I'm new to rails so any help would be greatly appreciated.

Update

create_table "blood_groups", force: :cascade do |t|
    t.string   "bloodgroup"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
end
create_table "patients", force: :cascade do |t|
    t.string   "firstName"
    t.string   "surname"
    t.datetime "created_at",    null: false
    t.datetime "updated_at",    null: false
    t.integer  "bloodgroup_id"
end
create_table "temperatures", force: :cascade do |t|
    t.integer  "patient_id"
    t.decimal  "temperature"
    t.datetime "dt"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
end

Controllers

Patient

class PatientsController < ApplicationController
  before_action :set_patient, only: [:show, :edit, :update, :destroy]

  # GET /patients
  # GET /patients.json
  def index
    @patients = Patient.all
  end

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

  # GET /patients/new
  def new
    @patient = Patient.new
  end

  # GET /patients/1/edit
  def edit
  end

  # POST /patients
  # POST /patients.json
  def create
    @patient = Patient.new(patient_params)

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

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

  # DELETE /patients/1
  # DELETE /patients/1.json
  def destroy
    @patient.destroy
    respond_to do |format|
      format.html { redirect_to patients_url, notice: 'Patient was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def patient_params
      params.require(:patient).permit(:firstName, :surname)
    end
end

Temperature

class TemperaturesController < ApplicationController
  before_action :set_temperature, only: [:show, :edit, :update, :destroy]

  # GET /temperatures
  # GET /temperatures.json
  def index
    @temperatures = Temperature.all
  end

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

  # GET /temperatures/new
  def new
    @temperature = Temperature.new
  end

  # GET /temperatures/1/edit
  def edit
  end

  # POST /temperatures
  # POST /temperatures.json
  def create
    @temperature = Temperature.new(temperature_params)

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

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

  # DELETE /temperatures/1
  # DELETE /temperatures/1.json
  def destroy
    @temperature.destroy
    respond_to do |format|
      format.html { redirect_to temperatures_url, notice: 'Temperature was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def temperature_params
      params.require(:temperature).permit(:patient_id, :temperature, :dt)
    end
end

Blood Group

class BloodGroupsController < ApplicationController
  before_action :set_blood_group, only: [:show, :edit, :update, :destroy]

  # GET /blood_groups
  # GET /blood_groups.json
  def index
    @blood_groups = BloodGroup.all
  end

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

  # GET /blood_groups/new
  def new
    @blood_group = BloodGroup.new
  end

  # GET /blood_groups/1/edit
  def edit
  end

  # POST /blood_groups
  # POST /blood_groups.json
  def create
    @blood_group = BloodGroup.new(blood_group_params)

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

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

  # DELETE /blood_groups/1
  # DELETE /blood_groups/1.json
  def destroy
    @blood_group.destroy
    respond_to do |format|
      format.html { redirect_to blood_groups_url, notice: 'Blood group was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def blood_group_params
      params.require(:blood_group).permit(:bloodgroup)
    end
end

Log

Started POST "/patients" for 81.144.132.166 at 2017-08-25 12:10:08 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"KnDRbEAL/f1DK6Gc9TcFNu0rRH5tahnGZiwLcx6FqH5iP7L5BsMOjH0yKR3630g/4ejCLUzilloj1rkO86wkAA==", "patient"=>{"firstName"=>"Test", "surname"=>"test", "bloodgroup_id"=>"7"}, "commit"=>"Create Patient"}
Unpermitted parameter: bloodgroup_id
   (0.1ms)  begin transaction
  SQL (41.7ms)  INSERT INTO "patients" ("firstName", "surname", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["firstName", "Test"], ["surname", "test"], ["created_at", "2017-08-25 12:10:08.979026"], ["updated_at", "2017-08-25 12:10:08.979026"]]
   (10.1ms)  commit transaction
Redirected to https://healthy-rails-conorsmalley.c9users.io/patients/6
Completed 302 Found in 125ms (ActiveRecord: 51.8ms)


Started GET "/patients/6" for 81.144.132.166 at 2017-08-25 12:10:09 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#show as HTML
  Parameters: {"id"=>"6"}
  Patient Load (0.4ms)  SELECT  "patients".* FROM "patients" WHERE "patients"."id" = ? LIMIT 1  [["id", 6]]
  Rendered patients/show.html.erb within layouts/application (0.4ms)
Completed 200 OK in 32ms (Views: 29.2ms | ActiveRecord: 0.4ms)

When I press submit to add bloodgroup to a patient i get the following logs

Started POST "/patients" for 81.144.132.166 at 2017-08-25 12:40:03 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Mkw0f3Yu6jXqzc+wBgbLxDDbW8GJY41ARBme0St/JIV6A1fqMOYZRNTURzEJ7obNPBjdkqjrAtwB4yysxlao+w==", "patient"=>{"firstName"=>"asdf", "surname"=>"ghjk"}, "Patient"=>{"bloodgroup_id"=>"3"}, "commit"=>"Create Patient"}
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "patients" ("firstName", "surname", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["firstName", "asdf"], ["surname", "ghjk"], ["created_at", "2017-08-25 12:40:03.623102"], ["updated_at", "2017-08-25 12:40:03.623102"]]
   (9.6ms)  commit transaction
Redirected to https://healthy-rails-conorsmalley.c9users.io/patients/8
Completed 302 Found in 15ms (ActiveRecord: 10.2ms)


Started GET "/patients/8" for 81.144.132.166 at 2017-08-25 12:40:03 +0000
Cannot render console from 81.144.132.166! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by PatientsController#show as HTML
  Parameters: {"id"=>"8"}
  Patient Load (0.2ms)  SELECT  "patients".* FROM "patients" WHERE "patients"."id" = ? LIMIT 1  [["id", 8]]
  Rendered patients/show.html.erb within layouts/application (0.5ms)
Completed 200 OK in 23ms (Views: 21.4ms | ActiveRecord: 0.2ms)

Solution

  • You have a few mistakes in your code which would be causing the problem

    1)

    belongs_to :bloodgroup
    

    should be

    belongs_to :blood_group
    

    as the model name is BloodGroup and the snake_case string of BloodGroup is blood_group not bloodgroup

    2)

    <%= collection_select(:Patient, :bloodgroup_id, BloodGroup.all, :id, :id, prompt: true) %>
    

    should be

    <%= collection_select(:patient, :blood_group_id, BloodGroup.all, :id, :id, prompt: true) %>
    

    Update:

    My db schema has the column bloodgroup_id not blood_group_id

    Then you need to specify your association with a custom foreign key else by default Rails will look for blood_group_id

    class Patient < ActiveRecord::Base
      has_many :temperatures
      belongs_to :blood_group, foreign_key: "bloodgroup_id"
    
      def fullname
        "#{firstName} #{surname}"
      end
    end
    

    Unpermitted parameter: bloodgroup_id

    You need to permit bloodgroup_id in the patient_params in order to save it to the DB

    def patient_params
      params.require(:patient).permit(:firstName, :surname, bloodgroup_id)
    end