朝活でカリキュラムを少しずつ進めておりますが、
実装課題でのユーザー登録のところで 、フォームに入力してもデータベースに保存されないエラーで躓きました。
自分の実装と解決後の学びをまとめようと思います。
やったこと
①deviseをインストール
②Userモデルを作成
③マイグレーションファイルにカラムを追加
④form_withでf.labelとf.text_fieldにusersテーブルのカラム名を入れてフォームを作成
⑤deviseのコントローラーにストロングパラメーターを反映
ここまで実装して動かしてみると、なぜかユーザー登録ができない。
Processing by Devise::RegistrationsController#create as TURBO_STREAM Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録"} Unpermitted parameter: :encrypted_password. Context: { controller: Devise::RegistrationsController, action: create, request: #<ActionDispatch::Request:0x0000000107068350>, params: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"ddd@ddd", "encrypted_password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "name"=>"aaa", "profile"=>"aaa", "occupation"=>"aaa", "position"=>"aaa"}, "commit"=>"新規登録", "controller"=>"devise/registrations", "action"=>"create"} } TRANSACTION (0.2ms) BEGIN User Exists? (1.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = BINARY 'ddd@ddd' LIMIT 1 TRANSACTION (0.2ms) ROLLBACK
emailの後にROLLBACKされている様子。
"encrypted_password"=>"[FILTERED]"となっており、パスワードあたりが怪しいのかな?と疑う。
確認したこと
モデルで変な制約をかけていないか
設計書通り記載。
user.rb
class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :name, presence: true validates :profile, presence: true validates :occupation, presence: true validates :position, presence: true end
migrationファイルの制約
設計書通り記載。
2024XXXXXXXXXX_devise_create_users
class DeviseCreateUsers < ActiveRecord::Migration[7.0] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" t.string :name, null: false t.string :profile, null: false t.text :occupation, null: false t.text :position, null: false # 省略
ストロングパラメーター
deviseはストロングパラメーターが使用できないので、代わりにdevise_parameter_sanitizerを使用。
自分で新しく追加したカラムを指定。
emailとpasswordについてはdeviseのデフォルトであるため記載不要。
application_controller.rb
class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile, :occupation, :position]) end end
passwordのカラム名
new.html.erb
<%= form_with model: @user, url: user_registration_path, local: true do |f| %> #省略 <div class="field"> <%= f.label :encrypted_password, "パスワード(6文字以上)" %><br /> <%= f.password_field :encrypted_password, id:"user_password", autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation, "パスワード再入力" %><br /> <%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %> </div> #省略 <% end %>
初めはストロングパラメーターで「:encrypted_password」も許可したらいいのか?
とかも思ったのですが、emailとpasswordについては元々deviseの機能として渡す機能はあるし、なんか気持ち悪いと思い・・・
deviseにおける新規登録やログイン機能について少し深掘りしてみることにしました。
encrypted_password
そもそも、encrypted_passwordとは、暗号化されたパスワードを意味しているらしい。
encrypted_passwordは、deviseを用いて作られるusersテーブルにあるカラムですが、
このencrypted_passwordはあくまで暗号化されたパスワードなので、暗号化されるまでは使えないカラム。
新規登録やログイン画面でパスワードはpasswordとして入力された値を受け取りdeviseへ送信する必要があるらしい。
passwordとpassword_confimationが同じかどうかをチェックし、
同じ(true)ならばencryped_passwordに暗号化した文字列を返すという機能がdeviseに実装されているそう。
なので、自分のコードを下記に書き換えてみました。
new.html.erb
<%= form_with model: @user, url: user_registration_path, local: true do |f| %> #省略 <div class="field"> <%= f.label :password, "パスワード(6文字以上)" %><br /> <%= f.password_field :password, id:"user_password", autocomplete: "new-password" %> </div> <div class="field"> <%= f.label :password_confirmation, "パスワード再入力" %><br /> <%= f.password_field :password_confirmation, id:"user_password_confirmation", autocomplete: "new-password" %> </div> #省略
そうすると、データーベースにしっかりと保存ができるようになりました。
まとめ
上記より、今回私がdeviseを用いてユーザー登録の実装をする際にエラーになってしまった原因は、
deviseを用いて作られるカラム、「encrypted_password」が、暗号化されたパスワードであり、
formで入力したパスワードが暗号化されていなかったことが原因でした。
パスワードは新規登録やログイン画面でpasswordとして入力された値を受け取りdeviseへ送信します。
deviseには
passwordとpassword_confimationが同じかどうかをチェックし、同じならばencryped_passwordに暗号化した文字列を返すという機能が実装されているので、
この機能を利用し、encrypted_passwordが暗号化され使用できるようになった感じと理解しました。
簡単に書きすぎてしまいましたが、
devise独特な?ルールやメソッドなどもあるので、しっかりと復習しようと思います。