noggy’s blog

自分用の備忘録です。。。

Devise.4 パスワードなしでユーザー情報を更新

Devise.3 で作成したregistrations_controller.rbで,ユーザー情報を編集画面(app/views/devise/registrations/edit.html.erb)で更新できるようにしたい。デフォルトの設定だと、ユーザー情報を更新するときにパスワードの入力が必要になるが、パスワードなしでも更新できるように設定する。

4.1 パスワードの解除

次のサイトを参考
easyramble.com

4.1.1 編集のView

app/views/devise/registrations/edit.html.erbから次のcurrent_password のフォーム削除するか、if false で囲み出力させないようにする。

<div class="field">
    <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
    <%= f.password_field :current_password, autocomplete: "current-password" %>
 </div>

<!-- 編集 -->
  <h4 class="text-center"><%= t('.title', resource: resource_name.to_s.humanize) %></h4>
  
  <!-- form_for -->
  <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
       
    <!-- 名前の入力らんを追加 -->
    <div class="form-group">
      <%= f.label :name %>
      <%= f.text_field :username, autofocus: true, class: "form-control", placeholder: '50字以内'%>
    </div>
  
    <!-- メールアドレス -->
    <div class="form-group">
      <%= f.label :email %>
      <%= f.email_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %>
    </div>
  
    <!-- パスワード -->
    <div class="form-group">
      <%= f.label :password %>
      <%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %>
  
      <small class="form-text text-muted"><%= t('.leave_blank_if_you_don_t_want_to_change_it') %></small>
    </div>
  
    <!-- 確認用パスワード -->
    <div class="form-group">
      <%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control'  %>
    </div>

    <div class="form-group">
      <%= f.submit t('.update'), class: 'btn btn-primary col-md-12' %>
    </div>
  <% end %>
  
  <!-- <p><%= t('.unhappy') %>? <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %>.</p> -->
  <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: { confirm: t('.are_you_sure') }, method: :delete %></p>
  <!-- <%= link_to t('.back'), :back %> -->
</div>

ルーティング

ルーティングを次のように変更

devise_for :users, controllers: { registrations: 'users/registrations' }

コントローラ

参考サイトにかいているように、Devise では Devise::RegistrationsController#update の中で、Devise::RegistrationsController#update_resource が呼び出されており、その実装はデフォルトでは以下のようになっている。

def update_resource(resource, params)
  resource.update_with_password(params)
end

なので、カスタマイズしたコントローラー(例えば Users::RegistrationsController < Devise::RegistrationsController)で、以下のようにオーバーライドする。

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

update_without_passwordは Devise で実装されており、パスワードなしで更新を行うメソッド。ただし、これだとパスワード自体を更新できない。なので、update_without_current_passwordとし、

class RegistrationsController < Devise::RegistrationsController

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

User モデルに update_without_current_password を実装

def update_without_current_password(params, *options)
    params.delete(:current_password)
    
    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
    end

    result = update_attributes(params, *options)
    clean_up_passwords
    result
  end

4.2 Devise::RegistrationsController#update をオーバーライド

updateアクション

 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)
 
    #if update_resource(resource, account_update_params)
    if resource.update_without_current_password(account_update_params)
      yield resource if block_given?
      if is_flashing_format?
        flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
          :update_needs_confirmation : :updated
        set_flash_message :notice, flash_key
      end
      sign_in resource_name, resource, :bypass => true
      respond_with resource, :location => after_update_path_for(resource)
    else
      clean_up_passwords resource
      respond_with resource
    end
  end
end

ここはまだ良く分かっていない。参考サイトによると、params.delete(:current_password) で current_password のパラメータを削除。if params[:password].blank? && params[:password_confirmation].blank? の行は、パスワード変更のためのパスワード入力フィールドとその確認フィールドの両者とも空の場合のみ、パスワードなしで更新できるようにするためとのこと。

userテーブルに追加したカラムをStrong Parametersに追加して更新できるようにする。

このままでは、userテーブルに追加したusernameやavatarが更新されないので、つぎのようにする。

protected
  
  # If you have extra params to permit, append them to the sanitizer.
  def configure_account_update_params
     devise_parameter_sanitizer.permit(:account_update, keys: [:username]) #追加
     devise_parameter_sanitizer.permit(:account_update, keys: [:email])
     devise_parameter_sanitizer.permit(:account_update, keys: [:attribute])
     devise_parameter_sanitizer.permit(:account_update, keys: [:password]) #追加
     devise_parameter_sanitizer.permit(:account_update, keys: [:avatar]) #追加
  end