About routing, If I do something like this:
resources :students
resources :teachers
I will get something like:
students GET /students(.:format) students#index
...
teachers GET /teachers(.:format) teachers#index
...
Changing to:
resources :students, controller: :users
resources :teachers, controller: :users
will give me:
students GET /students(.:format) users#index
teachers GET /teachers(.:format) users#index
Note that now, both resources are using the same controller Users
and the same action index
. But what I need, instead of using the same index
action, is the students
resource to use actions prefixed by students
like students_index
and teachers
resources prefixed by teachers
like teacher_index
.
In other words, I want bin/rails routes
to give me the following output:
students GET /students(.:format) users#students_index
teachers GET /teachers(.:format) users#teachers_index
I know that I can do the same with:
get 'students', to: 'users#students_index'
But there is a way to do the same with resources?
I don't think there's a way to do that with resources helper. What you could do (if it's only the index action you wanna override) is add an except, like this:
resources :students, controller: :users, except: [:index]
resources :teachers, controller: :users, except: [:index]
then, as you already suggested, do the individuals index actions like that:
get 'students', to: 'users#students_index', as: :student
get 'teachers', to: 'users#teachers_index', as: :teacher
Or you could reconsider the structure of your controllers... Good luck!
There is a far better way to do this as you might have surmised - inheritance.
# app/controllers/users_controller.rb
class UsersController < ApplicationController
delegate :singular, :plural, :param_key, to: :model_name
before_action :set_resource, only: [:show, :edit, :update, :destroy]
before_action :set_resources, only: [:index]
def initialize
@model_name = resource_class.model_name
super
end
def show
end
def index
end
def new
@resource = resource_class.new
set_resource
end
def create
@resource = resource_class.new(permitted_attributes)
if @resource.save
redirect_to @resource
else
set_resource
render :new
end
end
def edit
end
def update
if @resource.update(permitted_attributes)
redirect_to @resource
else
set_resource
render :edit
end
end
def destroy
@resource.destroy
redirect_to action: "index"
end
# ...
private
# Deduces the class of the model based on the controller name
# TeachersController would try to resolve Teacher for example.
def resource_class
@resource_class ||= controller_name.classify.constantize
end
# will set @resource as well as @teacher or @student
def set_resource
@resource ||= resource_class.find(params[:id])
instance_variable_set("@#{singular}", @resource)
end
# will set @resources as well as @teachers or @students
def set_resources
@resources ||= resource_class.all
instance_variable_set("@#{plural}", @resources)
end
def permitted_attributes
params.require(param_key).permit(:a, :b, :c)
end
end
# app/controllers/teachers_controller.rb
class TeachersController < UsersController
end
# app/controllers/students_controller.rb
class StudentsController < UsersController
end
# routes.rb
resources :students
resources :teachers
This lets you follow the regular Rails convention over configuration approach when it comes to naming actions and views.
The UsersController base class uses quite a bit of magic through ActiveModel::Naming both to figure out the model class and stuff like what to name the instance variables and the params keys.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.