記事にした経緯
私が作成中のアプリにログイン機能を実装するため、devise
を実装しようとした時に色々な課題があったため忘備録として記事にしました。
既存のusersテーブル
カラム名 | データ型 | 備考 |
---|---|---|
nickname | string | Notnull制約、presenceバリデーションあり |
gender | integer | Notnull制約、presenceバリデーションあり |
age | integer | Notnull制約、presenceバリデーションあり |
アプリを作成しdevise
をインストールします。
rails new devise_sumple -d postgreSQL (アプリ作成)
↓
rails db:create (データベース作成)
gem 'devise'
# ***** 以上を追加 *****
group :development, :test do
# 略
bundle install (gemをインストールする)
↓
rails g devise:install (アプリにdeviseをインストール)
↓
rails g devise User (deviseで認証するためのモデルを作成)
以下のファイルやコードが作成される
Running via Spring preloader in process 19712
invoke active_record
create db/migrate/20220716085821_devise_create_users.rb
create app/models/user.rb
insert app/models/user.rb
route devise_for :users
先ほど作成したマイグレーションファイル(devise_create_users.rb)に必要があれば修正を加えていく。
class DeviseCreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
## Database authenticatable :サインインしようとした人が本人か確かめる項目設定のためのカラム
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
## Trackable:サインインした回数や時間などを記録するカラム
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable:本人がサインインしているのかメールで確認するためのカラム
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable:何回もサインインに失敗した時の設定のためカラム
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
公式ページを見てみる
それぞれのカラムについての説明を上記コードに追加しておいた。
各自で不要なカラムはコメントアウトしてほしい。
私はパスワードのリセットする設定は不要なのでRecoverable:
のカラムと関係しているコードをコメントアウト、モデルの:recoverable,
を削除した。
#~省略
## Recoverable:パスワードをリセットするためのカラム
#t.string :reset_password_token
#t.datetime :reset_password_sent_at
#~省略
#add_index :users, :reset_password_token, unique: true
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:rememberable, :validatable
#:recoverable, 削除
end
これでマイグレーションファイルやUserモデルの編集が完了したためマイグレーション
を実行する。
rails db:migrate
↓上手くいくと下記が表示される
Running via Spring preloader in process 22019
== 20220716085821 DeviseCreateUsers: migrating ================================
-- create_table(:users)
-> 0.0460s
-- add_index(:users, :email, {:unique=>true})
-> 0.0030s
== 20220716085821 DeviseCreateUsers: migrated (0.0492s) =======================
この状態でログイン画面にアクセスして動作を確認しようとするとある問題が起こる
ログイン画面へアクセス→http://localhost:3000/users/sign_in
サインアップ画面に遷移する
以下のテストデータでサインアップしようとするとエラーが発生する
テスト用email | テスト用パスワード |
---|---|
test@example.com | password |
1 error prohibited this user from being saved:
Nickname can't be blank
nickname
カラムが空白で保存することを禁止したと表示される。
原因は既存のUserモデルでnickname``gender``age
にpresenceバリデーション
をかけていることだ。
なのでnickname``gender``age
カラムが空白にならないようにdeviseのviewファイルを編集していく。
その前にまずは、application.html.erb
にサインイン状態と、サインアウト状態でheaderの表示が変化するよう修正していく。
<!-- 略 -->
<body>
<!--以下を追加-->
<header>
<%= render "layouts/header" %>
</header>
<hr>
<!--以上を追加-->
<%= yield %>
</body>
<!-- 略 -->
ターミナルで_header.html.erb
を作成
touch app/views/layouts/_header.html.erb
作成した新規ファイルにサインインしている時、そうでない時の条件分岐
で表示を変えるよう修正していく
<% if user_signed_in? %>
<%# ログイン時 %>
<%= link_to "アカウント編集", edit_user_registration_path %>
<%= link_to "ログアウト", destroy_user_session_path, method: :delete, data: { confirm: "ログアウトしますか?" } %>
<%= "【ログイン中のアドレス】#{current_user.email}" %>
<% else %>
<%# 非ログイン時 %>
<%= link_to "新規登録", new_user_registration_path %>
<%= link_to "ログイン", new_user_session_path %>
<% end %>
解説
Q:user_signed_in?
とは
devise
が用意しているメソッド
で、ユーザー
がサインイン
しているかを確かめるためのメソッド
。
サインイン → ture
ログアウト → false
つまり上記のコード内容は
サインインしている?
tureの時は
アカウント編集 と ログアウト と ログイン中のアドレス を表示
そうでない時(ログアウト)は
新規登録
ログイン
を表示
と表わしている。
このままでは英語で分かりづらいので日本語化
を実装していく
まずは多言語対応設定
Gemfile
に以下のgemを追加する
# ログイン機能
gem 'devise'
# 以下を追加
# 多言語対応(Rails 6の場合)
gem 'rails-i18n', '~> 6.0'
gem 'devise-i18n'
# 以上を追加
group :development, :test do
# 略
新しくgemを追加したので正常に動作するよ依存関係を整える
bundle install
最後にconfig/application.rb
へ日本語対応
を設定
module LikeApp
class Application < Rails::Application
config.load_defaults 6.0
# 以下を追加
config.i18n.default_locale = :ja
config.time_zone = 'Asia/Tokyo'
# 以上を追加
# 略
これで一旦、日本語化
の実装が完了。サインイン画面で日本語化されているか確認
http://localhost:3000/users/sign_in
usersテーブルにカラムを追加したい
テーブルにカラムを追加する時は、既存のマイグレーションファイルに直接修正を加えるのではなく、新しくマイグレーションファイルを作成しマイグレーションを実行する
ますは新たなマイグレーションファイルを作成する
rails g migration クラス名 (AddColumnUsers ← Add_Column_追加先のテーブル名が一般的らしい)
↓(新しいマイグレーションファイルが作成される)
Running via Spring preloader in process 31808
invoke active_record
create db/migrate/20220717032330_add_column_users.rb
class AddColumnUsers < ActiveRecord::Migration[6.1]
def change
end
end
AddColumnUsers
というクラスの中にchange
というメソッドがある。
このchange
というメソッドの中に追加したいカラムを追記していく
私が今回作成したいUsersテーブルの構造は以下である。
カラム名 | データ型 | 備考 |
---|---|---|
string | deviseがデフォルトで作成済み | |
password | string | deviseがデフォルトで作成済み |
nickname | string | new! 新しく追加したい |
gender | integer | new! 新しく追加したい (enumで実装したい) |
age | integer | new! 新しく追加したい (enumで実装したい) |
2022/07/18 enum
については別の記事にする予定です
とりあえずstringでないことは一旦おいておいて進みます
以下の様に追記する
class AddColumnUsers < ActiveRecord::Migration[6.1]
def change
#カラムを追加するメソッド:追加先のテーブル名, :カラム名, :データ型
add_column :users, :nickname, :string
add_column :users, :gender, :integer
add_column :users, :nickname, :integer
end
end
データベースに反映するために以下のコマンドを実施
rails db:migrate
↓成功すると以下が表示される
Running via Spring preloader in process 33398
== 20220717032330 AddColumnUsers: migrating ===================================
-- add_column(:users, :nickname, :string)
-> 0.0025s
-- add_column(:users, :gender, :integer)
-> 0.0014s
-- add_column(:users, :age, :integer)
-> 0.0012s
== 20220717032330 AddColumnUsers: migrated (0.0052s) ==========================
データベースの更新をしたのでschemaファイル
の中身を確認する。
~省略~
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.datetime "remember_created_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "nickname"
t.integer "gender"
t.integer "age"
t.index ["email"], name: "index_users_on_email", unique: true
~省略~
よく見ると、nickename
、gender
、age
に制約
をかけていない、モデルにもバリデーションをかけいないので制約とバリデーションをかける作業を行なっていく。
Userモデルに空文字を防ぐバリデーションを追記。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:rememberable, :validatable
with_options presence: true do
validates :nickname
validates :gender
validates :age
end
end
新たに制約を追加するために新規マイグレーションファイルを作成
rails g migration ChangeColumnsAddNotnullOnUsers
作成されたマイグレーションファイルに``メソッドを使ってNotNull制約を追加
class ChangeColumnsAddNotnullOnUsers < ActiveRecord::Migration[6.1]
def change
change_column :users, :nickname, :string, null: false
change_column :users, :gender, :integer, null: false
change_column :users, :age, :integer, null: false
end
end
モデルにバリデーションをかけ、マイグレーションファイルには制約を追記した。
この状態でサインインや新規登録(性別:0
年齢:28
と入力)をしようとするとある問題が発生する
http://localhost:3000/users/sign_in
追加したカラム nickename
、gender
、age
に入力がないとエラーが起きてしまう。バリデーションやNotNull制約が作動しているためである。
なので新規登録画面にnickename
、gender
、age
の追加をする。
まずはdeviseのviewを生成する。
rails g devise:i18n:views
以下↓生成されるファイル
invoke Devise::I18n::SharedViewsGenerator
create app/views/devise/shared
create app/views/devise/shared/_error_messages.html.erb
create app/views/devise/shared/_links.html.erb
invoke Devise::I18n::MailerViewsGenerator
create app/views/devise/mailer
create app/views/devise/mailer/confirmation_instructions.html.erb
create app/views/devise/mailer/email_changed.html.erb
create app/views/devise/mailer/password_change.html.erb
create app/views/devise/mailer/reset_password_instructions.html.erb
create app/views/devise/mailer/unlock_instructions.html.erb
invoke i18n:form_for
create app/views/devise/confirmations
create app/views/devise/confirmations/new.html.erb
create app/views/devise/passwords
create app/views/devise/passwords/edit.html.erb
create app/views/devise/passwords/new.html.erb
create app/views/devise/registrations
create app/views/devise/registrations/edit.html.erb
create app/views/devise/registrations/new.html.erb
create app/views/devise/sessions
create app/views/devise/sessions/new.html.erb
create app/views/devise/unlocks
create app/views/devise/unlocks/new.html.erb
生成されたファイルの名前や中身をよく見てみると、エラーメッセージ系、メアドやパスワードを変更した時のメールでのお知らせ系などのviewファイルが揃っている。今回は追加したカラムに応じた入力フォームを作成したいのでapp/views/devise/registrations/new.html.erb
に変更を加えていく。
<h2><%= t('.sign_up') %></h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<!--下記追加-->
<div class="field">
<%= f.label :nickname, "ニックネーム" %><br />
<%= f.text_field :nickname, autofocus: true %>
</div>
<div class="field">
<%= f.label :gender, "性別" %><br />
<%= f.text_field :gender, autofocus: true %>
</div>
<div class="field">
<%= f.label :age, "年齢" %><br />
<%= f.text_field :age, autofocus: true %>
</div>
<!--上記追加-->
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
#省略
<%= render "devise/shared/links" %>
この状態で http://localhost:3000/users/sign_up にアクセスしフォームに入力してもまだエラーが表示されます。
ターミナルでエラーログを見てみるとnickename
、gender
、age
が許可されていないと表示されています。
# 省略
Unpermitted parameters: :nickname, :gender, :age
# 省略
どうやら勝手に作成したカラムに勝手にパラメーターを保存できないようにストロングパラメーターが起動した様子です。
解決方法について公式サイトのREADMEに記載されていました。https://github.com/heartcombo/devise#strong-parameters
こちらを参考にapp/controllers/application_controller.rb
を修正してみます。
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :gender, :age])
devise_parameter_sanitizer.permit(:account_update, keys: [:nickname, :gender, :age])
end
end
もう一度へアクセスして、各入力フォームに入力してみる(性別:0
年齢:28
と入力)
Yay! You’re on Rails!
と表示されたら
deviseにカラムの追加、入力フォームの追加が成功したということになる。
筆者はここから性別と年齢の入力をenumを使って実装してくが、とりあえずdeviseを使ってログイン機能の実装はここで一旦終了とする。
2022年7月17日 執筆中