こんにちは、nishi_talk(@nishi_talk)です。
今回は、deviseを使ってUserモデルに一対一のリレーションシップでユーザー情報を紐づけ方をご紹介します。
今回やりたかったこと
認証情報とユーザー情報を切り分けたかった。
セキュリティー上の観点とユーザー情報が増えれば増えるほど処理が重くなるかなーッと思って今回は以下の内容で対応。(あんまり意味ないのかなー)
ユーザー認証は「devise」
ユーザー情報は別テーブルの「userinfo」に格納する
前提条件
各種バージョン
- ruby 2.5.1
- Rails 5.2.0
$ bundle install
「devise」を記述済みで上記コマンドを実行済み
認証の設定
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base # CSRF保護をオンにする以下の1行を有効にします。 protect_from_forgery with: :exception # ログイン済ユーザーのみにアクセスを許可する before_action :configure_permitted_paramaters, if: :devise_controller? private def configure_permitted_paramaters #devise_parameter_sanitizer = 許可するパラメータを追加(railsのバージョンによって書き方が異なるので注意) devise_parameter_sanitizer.permit(:sign_up, keys: [:userinfo_attributes => [:user_id, :name, :body]]) devise_parameter_sanitizer.permit(:account_update, keys: [:userinfo_attributes => [:user_id, :name, :body]]) end end
- 【Rails】Deviseでのstrong parameters指定はdevise_parameter_sanitizer.forではなくdevise_parameter_sanitizer.permitを使うようになったらしい
- Rails+Deviseでユーザー名でサインインできるようにする
/app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController # # 省略 # #以下を追記 protected # If you have extra params to permit, append them to the sanitizer. def configure_sign_up_params devise_parameter_sanitizer.permit(:sign_up) do |params| params.permit(:email, :password, :password_confirmation, :current_password, userinfo_attributes: [:user_id,:name, :body]) end end # If you have extra params to permit, append them to the sanitizer. def configure_account_update_params devise_parameter_sanitizer.permit(:account_update) do |params| params.permit(:email, :password, :password_confirmation, :current_password, userinfo_attributes: [:user_id, :name, :body]) end end end
/app/models/user.rb
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable #has_one = 関連付け inverse_of = 双方の関連付け has_one :userinfo, dependent: :destroy, inverse_of: :user accepts_nested_attributes_for :userinfo, update_only: true end
userとuserinfoのリレーションを作成
/db/migrate/xxxxxxx_create_userinfos.rb
class CreateUserinfos < ActiveRecord::Migration[5.2] def change create_table :userinfos do |t| t.references :user, null: false t.string :name t.text :body t.timestamps end end end
マイグレートを実行
$ rails db:migrate
userモデルとの関連付け
/app/models/userinfo.rb
class Userinfo < ApplicationRecord belongs_to :user, inverse_of: :userinfo, optional: true end
各view側の設定
rails
<h2>Sign up</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <div class="field"> <%= f.label :password %> <% if @minimum_password_length %> <em>(<%= @minimum_password_length %> characters minimum)</em> <% end %><br /> <%= f.password_field :password, autocomplete: "off" %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "off" %> </div> <%= f.fields_for :userinfo, resource.build_userinfo || Userinfo.new do |info| %> <%= info.label :name %> <%= info.text_field :name%> <% end %> <div class="actions"> <%= f.submit "Sign up" %> </div> <% end %> <%= render "users/shared/links" %>
<h2>Edit <%= resource_name.to_s.humanize %></h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= devise_error_messages! %> <div class="field"> <%= f.label :email %><br /> <%= f.email_field :email, autofocus: true, autocomplete: "email" %> </div> <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div> <% end %> <div class="field"> <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br /> <%= f.password_field :password, autocomplete: "off" %> <% if @minimum_password_length %> <br /> <em><%= @minimum_password_length %> characters minimum</em> <% end %> </div> <div class="field"> <%= f.label :password_confirmation %><br /> <%= f.password_field :password_confirmation, autocomplete: "off" %> </div> <div class="field"> <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br /> <%= f.password_field :current_password, autocomplete: "off" %> </div> <%= f.fields_for :userinfo do |info| %> <%= info.label :name %> <%= info.text_field :name%> <% end %> <div class="actions"> <%= f.submit "Update" %> </div> <% end %> <h3>Cancel my account</h3> <p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p> <%= link_to "Back", :back %>