Question

Good day, community.

First of all, I'm a newbie in Rails. I did some thing with it in College 4 years ago, and now I decided to get back on it. Lots of things changed in version 4.

Anyway, I am experiencing issues with strong parameters. Here's what I have:

I'm using Ruby 2.1, Rails 4.1.

I am trying to create a form for a hockey match with parameters (id, team_a, team_b, arena, date, score_a, score_b). team is a table (id, name) and arena is a table (id, name).

When I pass the parameters from form to the controller, the json parameters seem to be okay. But, when it is converted into match_params it is missing some values from parameters from other table. For example, I am passing arena_id: 12, but it shows arena_id: as blank.

I've spent over 5 days on this thing. Any help appreciated.

Some of the code is bellow. Let me know if you need me to provide more information...

migration data

class CreateMatches < ActiveRecord::Migration
  def change
    create_table :matches do |t|
      t.references :team_a,     default: 1 # unknown
      t.references :team_b,     default: 1 # unknown
      t.references :arena,    default: 1 # unknown
      t.datetime :date
      t.integer :score_a
      t.integer :score_b
      t.timestamps
    end
    add_index :matches, :team_a_id
    add_index :matches, :team_b_id
    add_index :matches, :arena_id
  end
end

class CreateTeams < ActiveRecord::Migration
  def change
    create_table :teams do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end

class CreateArena < ActiveRecord::Migration
  def change
    create_table :arena do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end

match.rb (model)

class Match < ActiveRecord::Base
  belongs_to :team_a, :class_name => 'Team'
  belongs_to :team_b, :class_name => 'Team'
  belongs_to :arena
end

team.rb (model)

class Team < ActiveRecord::Base
  has_many :matches

  accepts_nested_attributes_for :matches
end

arena.rb (model)

class Arena < ActiveRecord::Base
  has_many :matches

  accepts_nested_attributes_for :matches
end

matches_controller.rb

class MatchesController < ApplicationController
  before_action :set_match, only: [:show, :edit, :score, :update, :destroy]

  include ActionView::Helpers::DateHelper

  def index
    # some code
  end

  def show
    # some code
  end

  def new
    @match = Match.new
    @teams = Team.all.order("name ASC")
    @arenas = Arena.all.order("name ASC")
  end

  # GET /matches/1/edit
  def edit
    # some code
  end

  def create

    puts YAML::dump(match_params)  # Checking passed params. Output is bellow

    @match = Match.new(match_params)  

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

  def update
  end

  def destroy
  end

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

  # Never trust parameters from the scary internet, only allow the white list through.
  def match_params
    params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], arena_id: [:id, :name])
  end

  public
end

teams_controller.rb

class TeamsController < ApplicationController
  before_action :set_team, only: [:show, :edit, :update, :destroy]

  layout :false

  def index
    @teams = Team.all
  end

  def show
  end

  def new
    @team = Team.new
  end

  def edit
  end

  def create
    @team = Team.new(team_params)

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

  def update
    respond_to do |format|
      if @team.update(team_params)
        format.json { head :no_content }
        format.html { redirect_to @team, notice: 'Team was successfully updated.' }
      else
        format.html { render action: 'edit' }
        format.json { render json: @team.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @team.destroy
    respond_to do |format|
      format.html { redirect_to teams_url }
      format.json { head :no_content }
    end
  end

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

  # Never trust parameters from the scary internet, only allow the white list through.
  def team_params
    params.require(:team).permit(:name)
  end
end

arenas_controller.rb

class ArenasController < ApplicationController
  before_action :set_arena, only: [:show, :edit, :update, :destroy]

  layout false

  def index
    @arena = Arena.all
  end

  def show
  end

  def new
    @arena = Arena.new
  end

  def edit
  end

  def create
    @arena = Arena.new(arena_params)

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

  def update
    respond_to do |format|
      if @arena.update(arena_params)
        format.json { head :no_content }
        format.html { redirect_to @arena, notice: 'Arena was successfully updated.' }
      else
        format.html { render action: 'edit' }
        format.json { render json: @arena.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @arena.destroy
    respond_to do |format|
      format.html { redirect_to arenas_url }
      format.json { head :no_content }
    end
  end

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

  # Never trust parameters from the scary internet, only allow the white list through.
  def arena_params
    params.require(:arena).permit(:name)
  end
end

matches/_match.html.erb

<%= form_for(@match, html: {role: 'form', class: 'form-horizontal'}) do |f| %>
    <% if @match.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(@match.errors.count, "error") %> prohibited this match from being saved:</h2>

          <ul>
            <% @match.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
          </ul>
        </div>
    <% end %>

    <%= f.label 'Home Team' %>
    <%= f.collection_select :team_a_id, @teams, :id, :name, {prompt: true}, {class: ''} %>

    <%= f.label 'Visitor Team' %>
    <%= f.collection_select :team_b_id, @teams, :id, :name, {prompt: true}, {class: ''} %>

    <%= f.label 'Arena' %>
    <%= f.collection_select :arena_id, @arenas, :id, :name, {prompt: true}, {class: ''} %>

    <%= f.label 'Date' %>
    <%= f.datetime_select :date, class: 'form-control' %>

    <%= f.submit value: 'Submit' %>

 <% end %>

And here's what I am getting in console after dumping data:

Started POST "/matches" for 127.0.0.1 at 2014-05-06 18:24:20 -0700
Processing by MatchesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}
User Load (0.5ms)  SELECT  `users`.* FROM `users`  WHERE `users`.`id` = 1  ORDER BY `users`.`id` ASC LIMIT 1
--- !ruby/hash:ActionController::Parameters
date(1i): '2014'
date(2i): '5'
date(3i): '6'
date(4i): '18'
date(5i): '24'
team_a_id:
team_b_id:
arena_id:
   (0.2ms)  BEGIN
  SQL (1.5ms)  INSERT INTO `matches` (`created_at`, `date`, `arena_id`, `team_a_id`, `team_b_id`, `updated_at`) VALUES ('2014-05-07 01:24:20', '2014-05-07 01:24:00', NULL, NULL, NULL, '2014-05-07 01:24:20')
   (0.2ms)  COMMIT
Redirected to http://localhost:3000/matches/90
Completed 302 Found in 13ms (ActiveRecord: 2.4ms)
Was it helpful?

Solution

Take a look at your match_params, and compare it to what parameters are being passed to your controller from your form.

def match_params
  params.require(:match).permit(:date, :score_a, :score_b, team_a_id: [:id, :name], team_b_id: [:id, :name], area_id: [:id, :name])
end

Parameters: {"utf8"=>"✓", "authenticity_token"=>"0RJjnpczVkp2unG9VITyHYC89ThgELn5kVE2wYRymBU=", "match"=>{"team_a_id"=>"24", "team_b_id"=>"27", "arena_id"=>"21", "date(1i)"=>"2014", "date(2i)"=>"5", "date(3i)"=>"6", "date(4i)"=>"18", "date(5i)"=>"24"}, "commit"=>"Update"}

You're permitting your arena_id in match_params as an array called area_id, with elements :id and :name. However, it's being passed from your form as just arena_id. You should change your match_params function to:

def match_params
  params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id, :arena_id)
end

Note that I've also changed :team_a_id and :team_b_id to be consistent with what's being passed in your parameters too, although it doesn't look like you're passing :score_a or :score_b. You should check out strong parameters in the rails guides for more information.

OTHER TIPS

Okay, I found my mistake. (Thanks to JKen13579)

I have put params in the wrong place.

It should be something like that:

def match_params
    params.require(:match).permit(:date, :score_a, :score_b, :team_a_id, :team_b_id , :arena_id)
end

def team_params
  params.require(:team).permit(:name, matches_params:[:id, :match_id, :name])
end

def arena_params
  params.require(:arena).permit(:name, matches_params:[:id, :match_id, :name])
end

It fixed the issue.

everyting but name will be removed when you call this:

params.require(:arena).permit(:name)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top