-
Notifications
You must be signed in to change notification settings - Fork 5.5k
How to manage users with a standard Rails controller
Basically you might want a standard user controller so the admin can edit and add users. Doing that in a straightforward way doesn't work. Devise creates routes inside the gem that are not explicit in routes.rb that interfere. You can see them with rake routes though.
There is another article with this title. Unfortunately it doesn't work. I tried it and still got password missing validation errors, even using the exact code in that other article. I also got other funky errors coming from the hidden Devise model validations. Various examples and instructions found through Google also didn'work. Some are very dense and it's difficult to see what the author's intention is. Some go back to 2012.
What follows is an end-run around Rails and around Devise. So it's not very Railsy. And not Devisey at all.
I would love to see a Devise config option because it's not a rare requirement.
What I did was create a new action in the controller and a new route for it, and connect the links on my views that normally connect to create to now call my route and action.
But that wasn't enough. Because Devise is listening and will grab any add you try to do and validate it through it's own code. So instead I just add the new user record with a sql insert.
Add these routes
post 'savenew', to: 'users#savenew'
Add this action to the user controller. Note that connect.quote prevents sql injection; sanitize itself just calls quote.
def savenew
sql = "insert into users (name,email, created_at,updated_at) values(
#{ActiveRecord::Base.connection.quote(user_params[:name])},
#{ActiveRecord::Base.connection.quote(user_params[:email])},now(), now())"
ActiveRecord::Base.connection.execute(sql)
redirect_to action: 'index'
end
View: change the form_for so that submit will go to the new route and action, not the default Rails one. new.html.erb
<%= form_for User, :url => {:action => "savenew"} do |f| %>
Using nested resources one can handle routing without stumbling on the routes generated by devise.
Generate a standard scaffolding under admin/users, then add a nested resource to routes.rb
devise_for :users
namespace :admin do
resources :users
end
Add skip_notifications
to your User
if you want to prevent admin actions sending out emails.
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable, :omniauthable
@skip = false
def skip_notifications!()
skip_confirmation_notification!
@skip = true
end
def email_changed?
return false if @skip
super
end
def encrypted_password_changed?
return false if @skip
super
end
end
In your new controller, you can remove the need for providing a password when an admin is updating a record with
before_action :allow_without_password, only: [:update]
# ...
private
def allow_without_password
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
end
This won't work for creating users though as the validation is on the user model. I would advise against creating users with no password at all, instead consider generating a password with Devise.friendly_token.first(16)
or similar methods and just leave it in.
Now if you want to skip notifications make sure to call it in the controllers that should not trigger change notifications:
def create
@user = User.new(user_params)
# don't send email notifications from admin interface
@user.skip_notifications!
# ...
end
def update
# don't send email notifications from admin interface
@user.skip_notifications!
# ...
end