Promise Preston
Promise Preston

Reputation: 28870

Rails: param is missing or the value is empty: admin

I am working on a Rails project and I am using namespacing for the models and controllers. That is the child models are put in a directory called user and the controllers are put in a directory called users.

For more clarity, I have a user model that has child admin and student models using Single Table Inheritance (STI). These child models inherit all the attributes of the parent model and can individually define their validations and methods and are placed in a directory called user.

I also have admins_controller and students_controller that use the admin model and the student model respectively. These controllers are placed in a directory called users.

Here's my code;

User model:

class User < ApplicationRecord
  has_secure_password
end

Admin model:

class User::Admin < User
end

Student model:

class User::Student < User
end

Admin Controller:

class Users::AdminsController < ApplicationController
  before_action :set_admin, only: [:show, :edit, :update, :destroy]

  # GET /admins
  # GET /admins.json
  def index
    @admins = User::Admin.all
  end

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

  # GET /admins/new
  def new
    @admin = User::Admin.new
  end

  # GET /admins/1/edit
  def edit
  end

  # POST /admins
  # POST /admins.json
  def create
    @admin = User::Admin.new(admin_params)

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

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

  # DELETE /admins/1
  # DELETE /admins/1.json
  def destroy
    @admin.destroy
    respond_to do |format|
      format.html { redirect_to users_admins_url, notice: 'Admin was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_admin
      @admin = User::Admin.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def admin_params
      params.require(:admin).permit(:email, :password, :role)
  end
end

Routes:

Rails.application.routes.draw do
  namespace :users do
    resources :admins
    resources :students
  end
end

index.html.erb:

<p id="notice"><%= notice %></p>

<h1>Admins</h1>

<table>
  <thead>
    <tr>
      <th>Email</th>
      <th>Password</th>
      <th>Role</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @admins.each do |admin| %>
      <tr>
        <td><%= admin.email %></td>
        <td><%= admin.password %></td>
        <td><%= admin.role %></td>
        <td><%= link_to 'Show', users_admin_path(admin) %></td>
        <td><%= link_to 'Edit', edit_users_admin_path(admin) %></td>
        <td><%= link_to 'Destroy', users_admin_path(admin), method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to 'New Admin', new_users_admin_path %>

show.html.erb:

<p id="notice"><%= notice %></p>

<p>
  <strong>Email:</strong>
  <%= @admin.email %>
</p>

<p>
  <strong>Password:</strong>
  <%= @admin.password %>
</p>

<p>
  <strong>Role:</strong>
  <%= @admin.role %>
</p>

<%= link_to 'Edit', edit_users_admin_path(@admin) %> |
<%= link_to 'Back', users_admins_path %>

_form.html.erb:

<% if @admin.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@admin.errors.count, "error") %> prohibited this admin from being saved:</h2>

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

<div class="field">
  <%= form.label :email %>
  <%= form.text_field :email %>
</div>

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

<div class="field">
  <%= form.label :role %>
  <%= form.text_field :role %>
</div>

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

new.html.erb:

<h1>New Admin</h1>

<%= form_with(model: @admin, url: users_admins_path, local: true) do |form| %>
  <%= render partial: 'form', admin: @admin, locals: { form: form } %>
<% end %>

<%= link_to 'Back', users_admins_path %>

edit.html.erb:

<h1>Editing Admin</h1>

<%= form_with(model: @admin, url: users_admin_path(@admin), local: true) do |form| %>
  <%= render partial: 'form', admin: @admin, locals: { form: form } %>
<% end %>

<%= link_to 'Show', users_admin_path %> |
<%= link_to 'Back', users_admins_path %>

However, when I try creating a new admin or updating an already existing admin, I get the error:

param is missing or the value is empty: admin

I have tried to debug the cause of the issue, but no luck yet. Any form of help will be highly appreciated.

Upvotes: 1

Views: 621

Answers (1)

Promise Preston
Promise Preston

Reputation: 28870

I finally figured it out after careful examination of my logs.

Here's how I solved it:

I examined the request parameters of the create action and I realized that it was of this format:

{"authenticity_token"=>"qJWlHz7Z5myTH3dwNIjjSOzRDY7JN+LoovaG+8dMBnGFRWImJKlWp8cgF7kwTqJXIxqU2fGVkqW9nhOAJ8vFIg==",
 "user_admin"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "role"=>""},
 "commit"=>"Create Admin"}

application trace screenshot

So the request parameters have a hash key of user_admin with values email, password and role, whereas I was passing the admin hash key in my admins_controller.

All I had to do was to modify the admins_params private method in my admins_controller:

From this:

def admin_params
  params.require(:admin).permit(:email, :password, :role)
end

To this:

def admin_params
  params.require(:user_admin).permit(:email, :password, :role)
end

and that solved the issue.

If you have such an issue, always endeavour to inspect your request parameters in your logs or your application trace. That will give you a lot of information that will help you to resolve the issue.

That's all.

I hope this helps

Upvotes: 1

Related Questions