1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

初心者向け devise導入について

Last updated at Posted at 2022-07-17

記事にした経緯

私が作成中のアプリにログイン機能を実装するため、deviseを実装しようとした時に色々な課題があったため忘備録として記事にしました。
既存のusersテーブル

カラム名 データ型 備考
nickname string Notnull制約、presenceバリデーションあり
gender integer Notnull制約、presenceバリデーションあり
age integer Notnull制約、presenceバリデーションあり

アプリを作成しdeviseをインストールします。

ターミナル
rails new devise_sumple -d postgreSQL (アプリ作成)
      ↓
rails db:create (データベース作成)
Gemfile
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
models/user.rb
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``agepresenceバリデーションをかけていることだ。
なのでnickname``gender``ageカラムが空白にならないようにdeviseのviewファイルを編集していく。
その前にまずは、application.html.erbにサインイン状態と、サインアウト状態でheaderの表示が変化するよう修正していく。

app/views/layouts/application.html.erb
 <!-- 略 -->
  <body>
    <!--以下を追加-->
    <header>
      <%= render "layouts/header" %>
    </header>
    <hr>
    <!--以上を追加-->
    <%= yield %>
  </body>
  <!-- 略 -->

ターミナルで_header.html.erbを作成

ターミナル
touch app/views/layouts/_header.html.erb

作成した新規ファイルにサインインしている時、そうでない時の条件分岐で表示を変えるよう修正していく

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を追加する

Gemfile
# ログイン機能
gem 'devise'

# 以下を追加 
# 多言語対応(Rails 6の場合)
gem 'rails-i18n', '~> 6.0'
gem 'devise-i18n'
# 以上を追加 
group :development, :test do
  # 略

新しくgemを追加したので正常に動作するよ依存関係を整える

ターミナル
bundle install

最後にconfig/application.rb日本語対応を設定

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テーブルの構造は以下である。

カラム名 データ型 備考
email 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ファイルの中身を確認する。

schema.rb
    ~省略~    
    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
     ~省略~

よく見ると、nickenamegenderage制約をかけていない、モデルにもバリデーションをかけいないので制約とバリデーションをかける作業を行なっていく。
Userモデルに空文字を防ぐバリデーションを追記。

models/user.rb
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
追加したカラム nickenamegenderage に入力がないとエラーが起きてしまう。バリデーションやNotNull制約が作動しているためである。
なので新規登録画面にnickenamegenderageの追加をする。
まずは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に変更を加えていく。

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 にアクセスしフォームに入力してもまだエラーが表示されます。
ターミナルでエラーログを見てみるとnickenamegenderageが許可されていないと表示されています。

ターミナル
# 省略
Unpermitted parameters: :nickname, :gender, :age
# 省略

どうやら勝手に作成したカラムに勝手にパラメーターを保存できないようにストロングパラメーターが起動した様子です。
解決方法について公式サイトのREADMEに記載されていました。https://github.com/heartcombo/devise#strong-parameters

こちらを参考にapp/controllers/application_controller.rbを修正してみます。

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日 執筆中

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?