前回の記事ではRailsアプリにdeviseでログイン機能を実装しました。 今回はプラクティスの終了条件である次の3つのページを追加します。
- ユーザー詳細:
users#show
- ユーザー編集:
users#edit
- ユーザー一覧:
users#index
実装対象のアプリはdevise gemのインストールする記事で使用したアプリです。
実装は次の手順で行います。
- ユーザー詳細、ユーザー一覧のルーティングを追加
- Userモデルのコントローラー、ビューを追加
- ユーザー詳細、ユーザー一覧ページへのリンクを追加
- usersテーブルへのカラム追加
- ユーザー詳細ページでユーザー情報を表示
- 追加したカラムをユーザー編集ページで編集可能にする
- ページネーション付きでユーザー一覧を表示
- ユーザー編集フォームに追加したカラムの入力フォームを追加する
- 追加したカラムをstrong_parametersに追加する
- ユーザ編集後のリダイレクト先を変更する
- カスタマイズ用contollerを設定
- リダイレクト先の変更用メソッドをオーバーライド
1. ユーザー詳細、ユーザー一覧のルーティングを追加
deviseのデフォルトでは認証機能付きモデルのshow
、index
アクションは設定されないので独自にルーティングの追加が必要になります。
config/routes.rb
に :only
でindexとshowアクションのみ追加します。
:only
についてはRailsガイドを参照してください。
# config/routes.rb Rails.application.routes.draw do devise_for :users # users#index, users#show を追加 resources :users, only: %i[index show] resources :books # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
ルーティングの追加を確認します。
$ rails route Prefix Verb URI Pattern Controller#Action user GET /users/:id(.:format) users#show users GET /users(.:format) users#index ...
以上でusers#index
、users#show
のルーティングを追加は完了です。
2. Userモデルのコントローラー、ビューを追加
追加したusers#index
、users#show
の2つのルーティング用のコントローラーとビューを追加します。
rails generate controller モデル名複数形 アクション名
でcontrollerファイルとviewファイルを作成できます。
show
、index
アクション用にルーティングを既に追加済みなので--skip-routes
オプションで不要なルーティング作成をスキップしてコマンドを実行します。
rails g controller で不要なルーティングの追加をスキップしたい - karlley's tech blog
今回はusers#index
、users#show
アクションが追加されたcontrollerファイルとそれぞれのルーティング用のviewファイルを作成します。
$ rails g controller users index show --skip-routes Running via Spring preloader in process 13318 create app/controllers/users_controller.rb invoke erb exist app/views/users create app/views/users/index.html.erb create app/views/users/show.html.erb invoke test_unit create test/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit invoke assets invoke scss create app/assets/stylesheets/users.scss
作成したcontrollerファイルとviewファイルを確認します。
$ ls -1 app/controllers/ application_controller.rb books_controller.rb concerns users users_controller.rb # 追加したcontrollerファイル $ ls -1 app/views/users/ confirmations index.html.erb # 追加したviewファイル mailer passwords registrations sessions shared show.html.erb # 追加したviewファイル unlocks
以上でUserモデルのコントローラー、ビューの追加は完了です。
3. ユーザー詳細、ユーザー一覧ページへのリンクを追加
追加したユーザー詳細、ユーザー一覧ページへのリンクを全viewファイルで読み込まれるapp/views/application.html.erb
に追加し、全ページでリンクを表示させます。
ログアウトのリンクはdevise gemのsession
モジュールに用意されているルーティングを指定します。
- ユーザ詳細:
users#show
- ユーザ一覧:
users#index
- ログアウト:
devise/sessions#destroy
追加するルーティングを確認します。
$ rails route ... Prefix Verb URI Pattern Controller#Action destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy # ログアウト user GET /users/:id(.:format) users#show # ユーザ詳細 users GET /users(.:format) users#index # ユーザ一覧 ...
app/views/application.html.erb
にlink_toメソッドでリンクを追加します。
# app/views/application.html.erb <!DOCTYPE html> <html> ... <body> ... # 下記を追加 <% if user_signed_in? %> <p>User: <%= current_user.email %></p> <%= link_to 'Profile', user_path(current_user) %> <%= link_to 'All Users', users_path %> <%= link_to 'Logout', destroy_user_session_path, method: :delete %> <% end %> <%= yield %> </body> </html>
ユーザー詳細ページへのリンクについて
user_path
のみidを引数として渡す必要があります。引数にcurrent_user
でログインユーザの情報を渡す事でRailsがidを取得してくれます。- 引数は
user_path(id: current_user.id)
としなくてもcurrent_user
だけでokです。 - ユーザ詳細は
http://localhost:3000/users/ユーザのid
のようなURLになります。
ログアウトのリンクについて
method: delete
で指定したパスにHTTPメソッドをDELETE
として送信します。
form タグにGET/POST以外のHTTPメソッドを指定する - karlley's tech blog
以上でusers#index
、users#show
ページへのリンクを追加は完了です。
4. usersテーブルへのカラム追加
今回作成するアプリではプロフィールページでメールアドレスとパスワードに加えて、郵便番号、住所と自己紹介文を表示できることが求められます。 郵便番号、住所、自己紹介文を保存するカラムが必要なのでusersテーブルへ下記カラムを追加します。
- 郵便番号:
zipcode
、integer型 - 住所:
address
、string型 - 自己紹介文:
profile
、text型
Railsでテーブルにカラムを追加する - karlley's tech blog
カラム追加用マイグレーションファイルを作成します。
$ rails g migration AddColumnsToUsers zipcode:integer address:string profile:text Running via Spring preloader in process 41547 invoke active_record create db/migrate/20220903210311_add_columns_to_users.rb
マイグレーションしてカラムを追加します。
$ rails db:migrate Running via Spring preloader in process 42834 == 20220903210311 AddColumnsToUsers: migrating ================================ -- add_column(:users, :zipcode, :integer) -> 0.0052s -- add_column(:users, :address, :string) -> 0.0004s -- add_column(:users, :profile, :text) -> 0.0003s == 20220903210311 AddColumnsToUsers: migrated (0.0061s) =======================
カラムの追加を確認します。
$ rails c Running via Spring preloader in process 46344 Loading development environment (Rails 6.1.6) irb(main):001:0> User.column_names (0.9ms) SELECT sqlite_version(*) => ["id", "email", "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at", "created_at", "updated_at", # ↓追加したカラム "zipcode", "address", "profile"]
5. ユーザー詳細ページでユーザー情報を表示
ユーザー詳細ページにDBから取得したユーザー情報を表示します。
rails g controller users index show --skip-routes
コマンドで作成した次の2つのファイルに追記します。
app/controllers/users_controller.rb
app/views/users/show.html.erb
# app/controllers/users_controller.rb class UsersController < ApplicationController def index; end def show # 下記を追記 @user = User.find(params[:id]) end end
# app/views/users/show.html.erb # 下記に書き換え <h1>Profile</h1> <p>Email: <%= @user.email %></p> <p>Zipcode: <%= @user.zipcode %></p> <p>Address: <%= @user.address %></p> <p>Profile: <%= @user.profile %></p> <% if @user == current_user %> <%= link_to 'Edit', edit_user_registration_path %> <%= link_to 'Delete', user_registration_path, data: { confirm: 'are you sure?' }, method: :delete %> <% end %>
params[:id]
で送信されたパラメータからidを取得して該当ユーザをfind
メソッドで検索後、@user
でビューに渡して各カラムの値を表示させています。if @user == current_user
とすることでユーザー編集、ユーザー削除のリンクを自分のプロフィールページのみ表示するようにしています。data: { confirm: 'are you sure?' }
とすることで削除時にare you sure?
が表示されます。
追加したユーザー編集ページへのリンクとユーザー削除のリンクは以下のルーティングを指定しました。
$ rails routes Prefix Verb URI Pattern Controller#Action ... 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 users GET /users(.:format) users#index user GET /users/:id(.:format) users#show ...
6. 追加したカラムをユーザー編集ページで編集可能にする
追加したカラムをユーザ編集ページで編集できるようにします。 ユーザ編集ページの表示にはdeviseが生成したviewファイルを使用します。
- ユーザー編集フォームに追加したカラムの入力フォームを追加する
- 追加したカラムをstrong_parametersに追加する
1. ユーザー編集フォームに追加したカラムの入力フォームを追加する
app/views/users/registrations/edit.html.erb
に追加したカラムの入力フォームを追加します。
# app/views/users/registrations/edit.html.erb <h2>Edit <%= resource_name.to_s.humanize %></h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= render "users/shared/error_messages", resource: resource %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> ... # ここからを追加↓ <div class="field"> <%= f.label :zipcode %><br /> <%= f.text_field :zipcode, autocomplete: "zipcode" %> </div> <div class="field"> <%= f.label :address %><br /> <%= f.text_area :address, autocomplete: "address" %> </div> <div class="field"> <%= f.label :profile %><br /> <%= f.text_area :profile, autocomplete: "profile" %> </div> # ここまで追記↑ <div class="actions"> <%= f.submit "Update" %> </div> <% end %> ...
ちなみに、autofocus: true はフォーム内に1つしか指定できません。
2. 追加したカラムをstrong_parametersに追加する
追加したカラム(zipcode
、address
、profile
)をstrong_parametersに追加しなとUPDATE処理がUnpermitted parameters:
で弾かれてしまうので設定を追加します。
configure_permitted_parameters
メソッドを定義してstrong_parametersにカラム追加を行います。
heartcombo/devise: Flexible authentication solution for Rails with Warden.
app/controllers/application_controller.rb
にconfigure_permitted_parameters
メソッドを定義します。before_action
で全てのアクションが実行される前にメソッドを実行します。
# app/controllers/application_controller.rb # frozen_string_literal: true class ApplicationController < ActionController::Base before_action :authenticate_user! # ここから追記↓ before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.permit(:account_update, keys: %i[zipcode address profile]) end # ここまで追記↑ end
configure_permitted_parameters
メソッド内で指定している:account_update
を変更することで他のアクションにも対応することができます。
:sign_in
: ログイン:sign_up
: サインアップ:account_update
: アップデート
devise導入からユーザーのプロフィール画面を作成するまで - Qiita
7. ページネーション付きでユーザー一覧を表示
kaminari gemを使ってページネーション付きでユーザー一覧を表示します。
kaminari gemについての雑なまとめ - karlley's tech blog
rails g controller users index show --skip-routes
コマンドで作成した次の2つのファイルへ追記します。
app/controllers/users_controller.rb
app/views/users/index.html.erb
# app/controllers/users_controller.rb class UsersController < ApplicationController def index # 下記を追記 @users = User.order(:id).page(params[:page]) end def show @user = User.find(params[:id]) end end
# app/views/users/index.html.erb # 下記に書き換え <h1>All Users</h1> <table> <thead> <tr> <th>Email</th> <th>Zipcode</th> <th>Address</th> <th>Profile</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @users.each do |user| %> <tr> <td><%= user.email %></td> <td><%= user.zipcode %></td> <td><%= user.address %></td> <td><%= user.profile %></td> <td><%= link_to 'Show', user_path(user) %></td> <% if user == current_user %> <td><%= link_to 'Edit', edit_user_registration_path %></td> <% end %> </tr> <% end %> </tbody> </table> <%= paginate @users %>
if user == current_user
とすることで自分のアカウントのみユーザー編集へのリンクを表示しています。<%= paginate @users %>
でkaminari gemを使ったページネーションが表示されます。
8. ユーザー編集後のリダイレクト先を変更する
deviseのデフォルトではユーザーのUPDATE処理後にルート(books#index
)へリダイレクトするようになっています。
このリダイレクト先をユーザー詳細ページ(users#show
)に変更します。
- カスタマイズ用コントローラーを設定
- リダイレクト先の変更用メソッドを定義
heartcombo/devise: Flexible authentication solution for Rails with Warden.
【Devise】アカウント登録、ログイン/ ログアウト、アカウント編集後のリダイレクト先の変更 - Qiita
1. カスタマイズ用コントローラーを設定
rails generate devise:controllers users
コマンドで生成したapp/controllers/users/
内の必要なcontrollerファイルを使えるように設定します。
未設定の場合だとルーティングではdeviseデフォルトのコントローラーを使用しているのを確認できます。
$ rails routes Prefix Verb URI Pattern Controller#Action root GET / books#index users GET /users(.:format) users#index user GET /users/:id(.:format) users#show ↓devise/...になっている new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit user_registration PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy POST /users(.:format) devise/registrations#create
使用するコントローラーをdevise/registrations
からusers/registrations
へ変更する設定をconfig/routes/rb
に追加します。
この記述はモジュール毎に行う必要があるので他のモジュールでもカスタマイズ用コントローラーを使う場合は別途設定を追加してください。
Rails.application.routes.draw do root 'books#index' # 下記に修正 devise_for :users, controllers: { registrations: 'users/registrations' } resources :users, only: %i[index show] resources :books # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end
使用するコントローラーが変更されたか確認します。
$ rails routes Prefix Verb URI Pattern Controller#Action root GET / books#index users GET /users(.:format) users#index user GET /users/:id(.:format) users#show ↓ 指定していないモジュールはそのまま new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create ↓ user/...に変更されている 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
これでカスタマイズ用コントローラーが使えるようになりました。
2. リダイレクト先の変更用メソッドをオーバーライド
作成したカスタマイズ用コントローラーでdeviseが用意しているリダイレクト先変更用メソッドをオーバーライドして動作を変更します。
- オーバーライドとは「継承した親クラスのメソッドの内容を子クラスで再定義すること」です。
after_update_path_for
メソッドをapp/controllers/users/registrations_controller.rb
内でオーバーライドします。
[devise]after_update_path_forメソッドをオーバーライドを有効にする方法
Class: Devise::RegistrationsController — Documentation for heartcombo/devise (main)
# app/controllers/users/registrations_controller.rb class Users::RegistrationsController < Devise::RegistrationsController ... # 下記を追記 protected def after_update_path_for(resource) user_path(resource) end ... end
これでユーザ編集後のリダイレクト先がユーザー詳細ページ(users#show
) に変更されます。
以上でdeviseを使ってユーザーページの追加は完了です。
まとめ
ユーザーページを追加するだけでものすごく時間が掛かってしまいました。 一つずつ公式のドキュメントを確認しながら実装するのはとても大変でしたが学びも多かったので良かったです。
参照
rails g controller で不要なルーティングの追加をスキップしたい - karlley's tech blog
form タグにGET/POST以外のHTTPメソッドを指定する - karlley's tech blog
Railsでテーブルにカラムを追加する - karlley's tech blog
HTML 選択要素 - HTML: HyperText Markup Language | MDN
heartcombo/devise: Flexible authentication solution for Rails with Warden.
devise導入からユーザーのプロフィール画面を作成するまで - Qiita
kaminari gemについての雑なまとめ - karlley's tech blog
heartcombo/devise: Flexible authentication solution for Rails with Warden.
【Devise】アカウント登録、ログイン/ ログアウト、アカウント編集後のリダイレクト先の変更 - Qiita
[devise]after_update_path_forメソッドをオーバーライドを有効にする方法
Class: Devise::RegistrationsController — Documentation for heartcombo/devise (main)