Reputation: 28870
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
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"}
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