やりたいこと
こんな感じでdeviseで作成したサインアップ機能にaccount名を入れられるようにしたい
課題
- マイグレーション時にaccount用のカラムを作成する必要がある
- deviseが標準で設定しているストロングパラメーターをいじる必要がある
マイグレーション時の対応
マイグレーションファイルに以下のように追記する
※今回はUniqueにしたかったので、その設定もしています
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :username, null: false #ここが追記
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
t.timestamps null: false
end
add_index :users, :username, unique: true #ここが追記
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
地味なポイント
unipueの設定はカラムの作成後、add_indexをする必要がある
t.string :username, null: false
という設定に属性を追記する感じで、
t.string :username, null: false, unipue: true
としたら行けるんじゃない?
と思ったのですが、仕様上できないようです。
ストロングパラメーターをいじる
これでカラムあるし、フォームに:username追加したら完了!
と思っていたのですが、バリデーションエラーが発生します。
普通であれば、自分で作成したコントローラーに自分で記述したストロングパラメーターを変更するだけで済むのですが、deviseで作成した場合users_controller
がないのでどうしよう…?となりました。
方法1:appllication_controllerをいじる
下記を記述すると解決しました。
before_action :configure_permitted_parameters, if: :devise_controller?
protected
# Deviseのストロングパラメーターをカスタマイズ
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
before_action :configure_permitted_parameters
はアクションの前にconfigure_permitted_parameters
を実行する、という記述
if: :devise_controller?
は微妙にわからなかったのですが、devise_controller?
はdeviseが定義しているメソッドでリクエストがDeviseに関連するコントローラアクションから来ているかどうかを確認するためのもの。
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
が今回のメイン。
devise_parameter_sanitizer
もdeviseが定義しているメソッドで、そのまんまストロングパラメーターの許可を追加するのに使うメソッドらしい。
さすがdevise…
ついでにsign_upだけでなく、update時にもバリデーションエラーが起きないように記述をすれば完了!
方法2:Deviseコントローラをカスタマイズ
方法1で完了したのですが、ちょっと悩んだのが、application_controllerに記載するという点。
特に気になったのは以下の2点
- application_controllerはすべてのcontrollerに継承されるからScope的にどうなんだろう…
- if文で条件分岐するくらいなら最初からdeviseコントローラーをいじればいいんじゃない?
ということで調べてみたのですが、まずdeviseコントローラーをいじるのは非推奨らしいです。
理由は、deviseのアップデートなどがあった際に上書きされてしまう可能性があるためとのこと。
回避する方法として、deviseコントローラーを継承したコントローラー作ること。
以下のような感じ。
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
devise_parameter_sanitizer.permit(:account_update, keys: [:username])
end
end
ただ上記の場合、自分で作成したコントローラーにリクエストを飛ばせるようにroutesもいじる必要がある。
devise_for :users, controllers: {
registrations: 'users/registrations'
}
どちらでも実現できそうですが、application_controllerのほうが一元管理という意味合いで良いみたい。
deviseのコントローラーから継承する形だとresistration,sessionなどそれぞれの機能毎にコントローラーを作成する形になるので、変更や修正があった場合に手間が増えるため。
メンテナンスまで考えて、今回はapplication_controllerの方にします。