【devise】パスワード入力無しのユーザー情報編集機能の実装

ユーザーの情報編集機能の実装をするにあたって思ったのは、アプリを使う側としてプロフィール編集するとき、「パスワードってなかなか変更しないよな」ということ。そこで、ユーザー情報を編集するときのパスワード入力を任意にするための実装をしてみた。

前提として、deviseを導入してユーザー管理機能を作成している。
deviseの導入に関してはこっち kyoro1210.hatenablog.com

環境

ruby 2.6.5
rails 6.0.0
devise 4.7.3

編集のときはパスワード入力を任意にするのに必要な実装

userモデルの生成やdeviseの導入は完了しているところから書いています。
ポイント兼目次的な感じ↓

  • deviseコントローラーを生成
  • application_controllerへの記述
  • registrasions_controllerへの記述
  • userモデルにおけるバリデーションの工夫
  • ルーティングへの記述

deviseコントローラー生成

registrations_controllerへの記述が必要なので、ターミナルでdevseコントローラー生成コマンドを実行

% rails g devise:controllers users

deviseのときのコントローラー生成コマンドはいつもとちょっと違うので注意

registrasions_controllerへの記述

目次の順番が前後しちゃうけど、registrasions_controllerが作られたので先にそっちを記述していく。
ここでは、この実装の目的である「編集のときはパスワードの入力はなくてもいいよ」っていう設定を書いておく。

class Users::RegistrationsController < Devise::RegistrationsController

  protected
  def update_resource(resource, params)
    resource.update_without_password(params)
  end
end

application_controllerへの記述

deviseを導入したときに、devise用ストロングパラメーターをセットしたことを思い出す。 そのとき、permitメソッドの第一引数には、:sign_up と記述した。これは新規登録の処理をするときのカラムの値を許可するため。 ということは、ユーザー情報更新のときの処理をするときバージョンも必要。
ということで、第一引数に :account_update を記述して、新規登録のときは必要ないけど、あとからプロフィール充実させるパターンのときは許可したいカラム名をここに追記していく。

class ApplicationController < ActionController::Base
  before_action :config_permitted_parameters, if: :devise_controller?
private
  def config_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
    devise_parameter_sanitizer.permit(:account_update, keys: [:image, :name, :text])
  end
end

userモデルにおけるバリデーションの工夫

基本的には、パスワードやeメールはdeviseがデフォルトで設定してくれているため、presence: true は記述しなくてもいいけど、今回はそのデフォルト機能が編集の情報入力にも適用されちゃうため、敢えて設定する。ただ、いつも通りに presence: true とだけ書くと意味ないので工夫が必要。
ユーザー作成(新規登録)の時だけパスワード入力必須にする on: :create をセットで記述してあげる。

validates :password, presence: true, on: :create

ルーティングへの記述

ここまでの実装でユーザー情報のパスワードは入力しなくても保存できるようなってるけど、実は、パスワードを変更したいと思って入力したときに保存されないようにもってしまっているため、パスワードの変更ができなくなっている。
この問題を解決するために、さっき生成して記述をしたregistrations_controllerを使用するようにdeviseに指示する必要がある。追記するのはルーティングのファイル。

devise_for  :users 、 controllers:{  registrations:'registrations'  }

これでいける!と思ったら...

挙動を確認してみると、エラーは出ないけど、ユーザー情報が更新されていない...。
なんでやあああ
保存されていないってことは、userコントローラーのupdateアクションが上手くはたらいてないってことだろうと思い、確認してみた。

def update
    @user = User.find(params[:id])
    if @user.valid?
      @user.update(update_params)
      redirect_to user_path(@user.id)
    else
      render :edit
    end
  end

  private
  def update_params
    params.require(:user).permit(:image, :name, :email, :password, :text)
  end

そしたらこっちのストロングパラメーターが間違ってました。updateのときはパスワードなくてもいいよって言っといてこっちに記述しちゃってた。 ついでにdeviseデフォルトのemailも記述の必要はないため、以下のように修正。

def update
    @user = User.find(params[:id])
    if @user.valid?
      @user.update(update_params)
      redirect_to user_path(@user.id)
    else
      render :edit
    end
  end

  private
  def update_params
    params.require(:user).permit(:image, :name, :text)
  end

これでいけました。

終わりに

今回ユーザー情報を編集するときのパスワード入力を任意にするという挙動は確認できて、実装としてはうまくいったけど、なんか間違ってるところある気もする。ただ、deviseのちょっとした応用チャレンジがうまくいってよかった。

ついでにJavaScriptも使ってみたかったので、編集でパスワードを変更したい方に向けた「パスワード変更フォーム展開ボタン」を非同期通信で実装した。今回書いた記事の内容からは逸れるけど、JavaScriptをもっと使えるようになるとアプリの使いやすさもかなり変わってくると思う。面白いけど少し難しい。今のとこそんな印象だけど、すごく興味ある。