LoginSignup
0
0

Deviseで複数モデルを使用したログインの実装

Posted at

やりたいこと

親ユーザーの認証ではデフォルトのemailとpasswordでの認証を使用してログイン。
子ユーザーの認証では、nicknameとパスワードで登録と認証ができるようにする。
また、子ユーザーを作成できるのは、親ユーザーのみ。
family(親ユーザー)

colum validation
email null:false
password null:false
family_name null:false

user(子ユーザ)

colum validation
nickname null:false
password null:false
family_id null:false

手順

root用のコントロ3ーラーとビューを作成

rails g controller home index

home_contoroller.rb
class HomeController < ApplicationController
  def index
  end
end

rootを変更

作成したhome/indexをrootに変更。

routes.rb
Rails.application.routes.draw do
  get 'home/index'
end

rails sを実行してからlocalhost:3000にアクセスして表示を確認する。

gemのインストール

Gemfileに追記

Gemfile
gem 'devise'

deviseをインストール

bundle install
bundle exec rails g devise:install 忘れがちなので注意

deviseの設定変更

initializers/devise.rb(変更前)
Devise.setup do |config|

#省略
 # ==> Scopes configuration
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
  # "users/sessions/new". It's turned off by default because it's slower if you
  # are using only default views.
  # config.scoped_views = true ←このコメントアウトをはずす
initializers/devise.rb(変更後)
Devise.setup do |config|

#省略
 # ==> Scopes configuration
  # Turn scoped views on. Before rendering "sessions/new", it will first check for
  # "users/sessions/new". It's turned off by default because it's slower if you
  # are using only default views.
  config.scoped_views = true ←このコメントアウトをはずす

上記はスコープ付きビューが必要な場合に必要です。今回は結局いらなかったかも?
公式リファレンス

deviseのモデルの作成

以下のコマンドを実行してdeviseのmodelを作成。
rails g devise family
rails g devise user
作成できたらcolumの追加をする。

db/migrate/****_devise_create_family.rb
class DeviseCreateFamilies < ActiveRecord::Migration[6.0]
  def change
    create_table :families do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""
      t.string :family_name,        null: false ←ここを追加

#省略
db/migrate/****_devise_create_user.rb
class DeviseCreateUsers < ActiveRecord::Migration[6.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 :nickname,           null: false, default: ""
      t.integer :family_id, null: false

# 省略

family_idは最初reference型で参照していたが、うまくいかなかったのでintegerにして直接数値を持ってくることにした。
migratetionファイルの編集が完了したら忘れずにマイグレーションを行う。
rails db:migrate
rails db:migrate:statusでmigrateできているかの確認をおこなう。

ルーティングの修正

ルーティングを変更しないと
変更しないと、familyとuserの使うコントローラーがぶつかってしまうので以下に変更。

routes.rb
Rails.application.routes.draw do

  get 'home/index'
  
  devise_for :families, controllers: {
    sessions:      'families/sessions',
    passwords:     'families/passwords',
    registrations: 'families/registrations'
  }
  devise_for :users, controllers: {
    sessions:      'users/sessions',
    passwords:     'users/passwords',
    registrations: 'users/registrations'
  }
root to: 'home/index'
end

これでルーティングが以下のようになる

                               Prefix Verb   URI Pattern                                                                              Controller#Action
                           home_index GET    /home/index(.:format)                                                                    home#index
                   new_family_session GET    /families/sign_in(.:format)                                                              families/sessions#new
                       family_session POST   /families/sign_in(.:format)                                                              families/sessions#create
               destroy_family_session DELETE /families/sign_out(.:format)                                                             families/sessions#destroy
                  new_family_password GET    /families/password/new(.:format)                                                         families/passwords#new
                 edit_family_password GET    /families/password/edit(.:format)                                                        families/passwords#edit
                      family_password PATCH  /families/password(.:format)                                                             families/passwords#update
                                      PUT    /families/password(.:format)                                                             families/passwords#update
                                      POST   /families/password(.:format)                                                             families/passwords#create
           cancel_family_registration GET    /families/cancel(.:format)                                                               families/registrations#cancel
              new_family_registration GET    /families/sign_up(.:format)                                                              families/registrations#new
             edit_family_registration GET    /families/edit(.:format)                                                                 families/registrations#edit
                  family_registration PATCH  /families(.:format)                                                                      families/registrations#update
                                      PUT    /families(.:format)                                                                      families/registrations#update
                                      DELETE /families(.:format)                                                                      families/registrations#destroy
                                      POST   /families(.:format)                                                                      families/registrations#create
                     new_user_session GET    /users/sign_in(.:format)                                                                 users/sessions#new
                         user_session POST   /users/sign_in(.:format)                                                                 users/sessions#create
                 destroy_user_session DELETE /users/sign_out(.:format)                                                                users/sessions#destroy
                    new_user_password GET    /users/password/new(.:format)                                                            users/passwords#new
                   edit_user_password GET    /users/password/edit(.:format)                                                           users/passwords#edit
                        user_password PATCH  /users/password(.:format)                                                                users/passwords#update
                                      PUT    /users/password(.:format)                                                                users/passwords#update
                                      POST   /users/password(.:format)                                                                users/passwords#create
             cancel_user_registration GET    /users/cancel(.:format)                                                                  users/registrations#cancel
                new_user_registration GET    /users/sign_up(.:format)                                                                 users/registrations#new
               edit_user_registration GET    /users/edit(.:format)                                                                    users/registrations#edit
                    user_registration PATCH  /users(.:format)                                                                         users/registrations#update
                                      PUT    /users(.:format)                                                                         users/registrations#update
                                      DELETE /users(.:format)                                                                         users/registrations#destroy
                                      POST   /users(.:format)                                                                         users/registrations#create
                             families GET    /families(.:format)                                                                      families#index
                               family GET    /families/:id(.:format)                                                                  families#show
                                 root GET    /                                                                                        home#index

deviseのビューの作成

rails g devise:views families
rails g devise:views usersコマンドでviewを作成

コントローラーを作成

次のコマンドでコントローラーを作成

bundle exec rails generate devise:controllers users
bundle exec rails generate devise:controllers families

いったん表示されるか確認

localhost:3000/users/sign_in
localhost:3000/families/sign_in
でそれぞれのページが表示されるか確認する。

viewファイルの編集

今回userでの登録はnicknameとパスワードのみで行いたい。
そのためフォームをいじる。

app/views/users/regstrations/new.htm.erb
        <%= form_with model: @user, url: user_registration_path, method: :post, local: true do |f| %>

        <div class="field">
          <%= f.label :nickname, "ユーザー名" %><br />
          <%= f.text_field :nickname, id:"nickname" %> 
        </div>

        <div class="field">
          <%= f.label :password, "パスワード(6文字以上)" %><br />
          <%= f.password_field :password, id:"family_password", autocomplete: "new-password" %>
        </div>

        <div class="field">
          <%= f.label :password_confirmation, "パスワード再入力" %><br />
          <%= f.password_field :password_confirmation, id:"family_password_confirmation", autocomplete: "new-password" %>
        </div>

        <div class="actions">
          <%= f.submit "新規登録", class: :form__btn  %>
        </div>
      <% end %>

こんな感じにしておく。

また、familyの登録には、family_nameを加えたいので、viewをいじる

app/views/families/regstrations/new.htm.erb
<%= render "families/shared/error_messages", resource: resource %>

        
        <%= form_with model: @family, url: family_registration_path, method: :post, local: true do |f| %>

        <div class="field">
          <%= f.label :email, "保護者メールアドレス" %><br />
          <%= f.email_field :email, id:"family_email", autofocus: true, autocomplete: "email" %>
        </div>

        <div class="field">
          <%= f.label :password, "パスワード(6文字以上)" %><br />
          <%= f.password_field :password, id:"family_password", autocomplete: "new-password" %>
        </div>

        <div class="field">
          <%= f.label :password_confirmation, "パスワード再入力" %><br />
          <%= f.password_field :password_confirmation, id:"family_password_confirmation", autocomplete: "new-password" %>
        </div>

        <div class="field">
          <%= f.label :family_name, "家族名" %><br />
          <%= f.text_field :family_name, id:"family_name" %> 
        </div>
        
        <div class="actions">
          <%= f.submit "新規登録", class: :form__btn  %>
        </div>
      <% end %>

スタイルシートとかいろいろいじっていい感じにするのに時間がかかったけどそこは、本件と関係ないのではしょる。

modelファイルの編集

まずはfamilyのモデルから

models/family.rb(編集後)
class Family < ApplicationRecord

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :authentication_keys => [:email]
         validates :family_name, presence: true

         has_many :users, dependent: :destroy
end

:authentication_keys => [:email]を追記して、familyモデルではemailでの認証のままにしておく。(書かなくてもいいと思って、書かないでやったらエラーが発生したので、カスタムする場合はそれぞれのモデルに書いた方がよさそうです。)
また、has_many :users, dependent: :destroyでuserモデルとアソシエーションを記述してfamilyに対してuserを紐づけておく。

models/user.rb(編集後)
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :authentication_keys => [:nickname] 

  validates :nickname, presence: true, uniqueness: true 
  belongs_to :family

  def email_required?
    false
  end

  def email_changed?
    false
  end

end


:authentication_keys => [:nickname]で認証キーを[:nickname]にオーバーライドします。
nicknameで認証するために一意のバリデーションをかけておきます。
belongs_to :familyでアソシエーションを行い、familyと紐づけておきます。

def email_required?
    false
  end

  def email_changed?
    false
  end

emailをキーにしていないため、データの更新や変更にemailを必須とするとエラーの原因になるためバリデーションを無効化させています。

コントローラーの編集

family_nameが受け取れるように、before_actionでpramaterの要素を追加してあげる。

contorollers/families/registrations_controller.rb

before_action :configure_sign_up_params, only: [:create]

private

   def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :password, :password_confirmation, :family_name])
  end
end

family次にログインの実装のためにsession_contoroller.rbファイルを編集する。

controllers/families/session_contoroller.rb
class Families::SessionsController < Devise::SessionsController

 before_action :configure_sign_in_params, only: [:create]

 protected
  def configure_sign_in_params
    devise_parameter_sanitizer.permit(:sign_in, keys: [:email])
  end
end

emailが認証キーになるように記述。デフォルト設定のままなので必要ないと思ったが、なぜかemailがpermitできないエラーが出てしまったので、記述した。記述することでエラーが解消された。

次にuserコントローラーの設定

controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
before_action :authenticate_family!, if: :devise_controller?
before_action :configure_sign_up_params, only: [:create]

  def new
    authenticate_family!
    @user = User.new
  end

  # ユーザーを登録するアクション
 def create
    authenticate_family!
    super do |user|
       if family_signed_in?
          user.family = current_family
          user.save! 
       end
    end
  end
  private
  def configure_sign_up_params
 
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :password, :password_confirmation, :family_id])
  end

  def sign_up(resource_name, resource)
    if !current_family
       sign_in(resource_name, resource)
    end
  end
end


before_action :authenticate_family!, if: :devise_controller?

これによりfamilyでログインしていない場合userの新規登録ができないようにしています。

before_action :configure_sign_up_params, only: [:create]
private
  def configure_sign_up_params
 devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :password, :password_confirmation, :family_id])
  end

これにより、:family_idと:nicknameを受け取ることが可能になり、emailは不要となる。

  def new
    authenticate_family!
    @user = User.new
  end

  # ユーザーを登録するアクション
  def create
    authenticate_family!
    super do |user|
       if family_signed_in?
          user.family = current_family
          user.save! 
       end
    end
  end

authenticate_family!でfamilyで認証していないとアクションができないようにした。
paramaterをカスタマイズしたのでアクションの記述をオーバーライドした。createアクションは、superで元の処理を使いつつ、family_idをどうしてもparamsに含むことができずにエラーが出てしまったので、追加した。

controllers/users/session_contoroller.rb
class Users::SessionsController < Devise::SessionsController

before_action :configure_sign_in_params, if: :devise_controller?
  def create
      super
  end
  protected
   def configure_sign_in_params
     
 
 devise_parameter_sanitizer.permit(:sign_in, keys: [:nickname, :password, :remember_me])
  end
end

sessionのparamsに[:nickname]が含まれるようにするためにbefore_actionで記述。

これで無事に実装完了しました。

終わりに

まとめて書くとこんだけかと思いますが、これを実装するまでに1日丸々費やしました。
間違いや、解釈違いも多いかと思いますので、指摘していただけたらと思います。これから実装する人の一助になれば幸いです。

参考にした記事など

0
0
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
0
0