Search code examples
ruby-on-railsactioncontroller

Not sure what to put for strong parameters in nested resource


I'm trying to debug a controller that just won't play ball. I've narrowed it down to the strong parameters not working properly and so I've tried it out on the rails console.

params = ActionController::Parameters.new({
  "user"=> {
    "login"=>"username",
    "password"=>"[FILTERED]"
   },
  "staff_id"=>"1"
})

This returns, as you'd expect:

=> {"user"=>{"login"=>"username", "password"=>"[FILTERED]"}, "staff_id"=>"1"} 

So, I attempted to filter the parameters, like so...

params.require(:staff_id)
=> "1" 

> params.require(:user).permit(:password,:login)
=> {"password"=>"[FILTERED]", "login"=>"username"} 

That looks ok.

In my controller, I have:

def create    
  @staff=Staff.find(params[:staff_id])
  @user = @staff.create_user(reg_params[:user])
  DISASTER

Now, at the point of the disaster, the user object @user should have a login and password set but It doesn't. I've tried logging the @user object at that point, it's properties are nil.

Why isn't my user object being created properly? If I remove the DISASTER, it creates a database record with blank fields, except for the timestamps.

Models:

class Staff < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_one :staff
end

Schema:

ActiveRecord::Schema.define(version: 20150909102012) do
  create_table "staff", force: :cascade do |t|
    t.string   "qualifications"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
    t.integer  "user_id"
  end

  add_index "staff", ["users_id"], name: "index_staff_on_users_id"

  create_table "users", force: :cascade do |t|
    t.string   "login"
    t.string   "username"
    t.string   "password"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
end

It routes to the custom controller ok. Here's the route:

Rails.application.routes.draw do 
  resources :staff do
    resource :user, shallow: true, controller: 'staff_register'
  end

  resources :users  

There's nothing else in the app because it's just an experiment to help me work on a bigger problem I've been struggling with.

Basically, I want to be able to link a staff model to a user model. I think I've done that ok, I just need help figuring out the strong parameters bit.

EDIT: here's the form for the nested resource:

<%= form_for @user, :url => staff_user_path(@staff) do |f| %>    
  <% if @user.errors.any? %>   
    <div id="error_explanation">    
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">    
    <%= f.label :login %><br>    
    <%= f.text_field :login %>    
  </div>  

  <div class="field">    
    <%= f.label :password %><br>    
    <%= f.text_field :password %>    
  </div>

  <div class="actions">    
    <%= f.submit %>    
  </div>

<% end %>

Solution

  • In this line:

    @user = @staff.create_user(reg_params[:user])
    

    reg_params already gives you hash with all the permitted parameters:

    {"password"=>"[FILTERED]", "login"=>"username"} 
    

    There is no user key there, hence reg_params[:user] is just nil. Instead you need to do:

    @user = @staff.create_user(reg_params)
    

    Now, you do not need to worry about staff_id here, as you are executing create_user method on already existing @staff model. This method will take care of the association.