なんの話?
deviseを導入してuser情報を管理していたが、編集をしようと思ったら一筋縄でいかなかった話です。
自分がやったこと
- usersコントローラーにeditアクション、updateアクションをそれぞれ記述
- viewファイルを変更
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update(user_params)
redirect_to root_path, dark: 'ユーザー情報の編集ができました'
else
flash[:danger] = 'ユーザー情報の編集ができませんでした'
render :edit
end
end
すごーく単純な作業だからすぐできると思っていた。
その結果。
色々と調べてみたところ、deviseのコントローラーを新しく追加すればいけるようだったのでやってみる。
1・まずはコントローラーを作成
% rails g devise:controllers users
⬇️⬇️⬇️実行結果⬇️⬇️⬇️
Running via Spring preloader in process 7028
create app/controllers/users/confirmations_controller.rb
create app/controllers/users/passwords_controller.rb
create app/controllers/users/registrations_controller.rb
create app/controllers/users/sessions_controller.rb
create app/controllers/users/unlocks_controller.rb
create app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================
Some setup you must do manually if you haven't yet:
Ensure you have overridden routes for generated controllers in your routes.rb.
For example:
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions'
}
end
===============================================================================
2・作成したregistrations_controller
に記述
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_account_update_params, only: [:update]
protected
def update_resource(resource, params)
resource.update_without_password(params)
end
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update,
keys: [:nickname, :gender_id, :profile, :genre_id])⬅️keys:の中身はカラム名に
end
end
devise_parameter_sanitizer.permit(:account_update, keys: [:nickname, :gender_id, :profile, :genre_id])
このkeys:の部分には自分のDBのカラム名を設定。
email、password、password_confirmationは書かない。
アカウントをupdate(編集)する時に、どのデータの受け取りを許可させるか指定する。
3・ルーティングに記述
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: 'users/registrations'
}
〜以下略〜
devise_for :users
の部分に追加。
4・userモデルに記述
class User < ApplicationRecord
〜中略〜
PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze
#on: :create ➡️ createアクションの時にだけvalidatesをかける
validates :password, format: { with: PASSWORD_REGEX }, on: :create
validates :password, confirmation: true, on: :create
end
passwordとpassword_confirmationの部分にon: :create
を追記。
createアクションを経由するときだけにバリデーションをかけるようにする。
つまりupdateアクションの時にpasswordを必要としない(バリデーションをかけない)ように変更。
5・viewファイルを変更
入力フォームを空にして送信すると突然見たことがないviewファイルが読み込まれる。
app> views> devise> registration> edit.html.erb
どうやらこのファイルを読み込んでいるようなのでviewファイルを変更する。
app> views> users> edit.html.erb
ここに作ったものをそのままコピペ。
この時にemail、password、password_confirmationの項目は表示させないようにするため削除しておく。
6・flashメッセージの設定
def update
@user = User.find(params[:id])
if @user.update(user_params)
redirect_to root_path, dark: 'ユーザー情報の編集ができました'
else
flash[:danger] = 'ユーザー情報の編集ができませんでした'
render :edit
end
end
usersコントローラーを経由しなくなりflashメッセージが出なくなってしまったので、registrations_controller
に記述する。
〜略〜
# PUT /resource
# def update
# super
# end
〜略〜
updateアクションはデフォルトでこうなっていた。
superで呼び出している内容は
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
resource_updated = update_resource(resource, account_update_params)
yield resource if block_given?
if resource_updated
set_flash_message_for_update(resource, prev_unconfirmed_email)
bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
respond_with resource, location: after_update_path_for(resource)
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
これを書き換えたらこうなった。
# PUT /resource
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
resource_updated = update_resource(resource, account_update_params)
yield resource if block_given?
if resource_updated
redirect_to @user, dark: 'ユーザー情報の編集ができました'
bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
else
flash[:danger] = 'ユーザー情報の編集ができませんでした'
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
elseの部分は加えるだけで良かったがifのほうはそのまま加えると
AbstractController::DoubleRenderError in Users::RegistrationsController#update
というエラーが出て、ダブルレンダリングしてるよと怒られてしまった。
該当の部分を削除して完成し、ようやくやりたいことが実現できた。
修正部分などありましたらご指摘下さい。
参考にさせて頂きました
[heartcombo/devise]GitHub
ありがとうございました。