18
23

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 5 years have passed since last update.

deviseから複数モデルへの保存

Last updated at Posted at 2019-04-02

実現したかったこと

deviseのアカウント登録の際に2つのモデルに記録させたい。
(ユーザー情報と会社情報)

どうして?

下記の問題が起こるため。

  • Followテーブル
    CampanyとUserそれぞれで User Aさんをフォローしたかった。

問題箇所

フォロー機能実装の際にCampany(モデル)とUser(モデル)で登録したユーザーは同じ番号のidを持っている。
┗Campanyページからアカウント登録を行った際の1番目のユーザーidは1番。
┗Userページからアカウントアカウント登録を行った際の1番目のユーザーidは1番。
Followテーブルにfollowerとして情報を記録する際に後々の紐付けのためにidを残したいがCampanyとUserはそれぞれ違うテーブルから来たid情報。
id 1となり重複してしますので、この方法は良くなかった。
スクリーンショット 2019-04-02 21.58.28.png

考えた解決方法

法人(Campany)もUserとみなしUserテーブルに登録する。
ただし法人情報は子モデルとしてCampaniesテーブルに登録する。

準備

deviseのデフォルト設定カラムに任意カラム追加

deviseのregistrations_controller.rb
  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up,
      keys: [:name, :category_id, campany_attributes: [:campany_name, :campany_logo, :category_id, :campany_url, :tel, :staff_last_name_kana, :staff_first_name_kana]])
  end

campany_attributes: [:campanyレコードのカラム名, :campanyレコードのカラム名, ]
モデル名_attributesとすることで親モデルに紐づけている。
(補足:campany_attributesのcampanyはhas_one関係なら単数、has_manyなら複数)

アソシエーション設定

user.rb
  # userモデルの中でcampanyモデルへも同時書き込み
  has_one :campany, inverse_of: :user
  accepts_nested_attributes_for :campany
  # Userモデル中でcampanyモデルへも書き込む
  belongs_to :user, inverse_of: :campany

今回はnewアクションはuser登録で使用している。
法人登録は別画面にしたかったので下記のルートで準備。

routes.rb
    get '/campany/new' => 'users/registrations#campany' ,as: :new_campany

参考にした記事では
@user.campany.buildでネストモデルの構造にできるとあったが、この記述だがうまく行かなかった。
下記の記法にすることで解決できた。
またcreateアクションはdeviseの機能を上書きするのでsuper以下に記述する。
最後にuser.saveとしなければ子モデル(campany)には保存されないので忘れずに。

deviseのregistrations_controller.rb
  # 企業の登録フォーム
  def campany
    @user = User.new
    @user.build_campany
  end

  def create
    super
    user = User.new(configure_sign_up_params)
    user.save
  end

注意:
子モデルへのデータの渡し方は下記のようにf.fields_forを使用する。
上下のようにインデントがきちんと整っていれば実現したいレイアウトに合わせてUserモデル、Campanyモデルへ渡すデータの位置がバラバラに置いても良い。
(親formはform_forであること)

campany.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 "会社名" %><br />
    <%= f.text_field :name, autocomplete: "off" %>
  </div>

  <%= f.fields_for :campany do |campany| %>
    <div class="field">
      <%= campany.label "会社URL" %><br />
      <%= campany.text_field :campany_url, autocomplete: "off" %>
    </div>

    <div class="field">
      <%= campany.label :tel %><br />
      <%= campany.telephone_field :tel, autocomplete: "off" %>
    </div>
  <% end %>

  <div class="field">
    <%= f.label "カテゴリー" %><br />
    <%= f.collection_select :category_id, Category.all, :id, :name, {prompt: "選択してください"}%>
  </div>

  <%= f.fields_for :campany do |campany| %>
    <div class="field">
      <%= campany.label "ご担当者様 姓(カナ)" %><br />
      <%= campany.text_field :staff_last_name_kana, autocomplete: "off" %>
    </div>

    <div class="field">
      <%= campany.label "ご担当者様 名(カナ)" %><br />
      <%= campany.text_field :staff_first_name_kana, autocomplete: "off" %>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "off" %>
  </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>

  <div class="actions">
    <%= f.submit t('.sign_up')%>
  </div>
<% end %>

<%= render "devise/shared/links" %>

スクリーンショット 2019-04-02 20.12.45.png

無事に2つのモデルに保存されました。

失敗したこと

registrations_controllerの中でcampanyメソッドを最初はnew_campanyという名前にしていた。
エラーで**new_campany_path?**と言われたがそんなはずはない。
そもそもメソッド名がprefixパスでエラー文吐き出すってどういう意味?
悩むこと3時間、予約語 のキーワードが頭をよぎった。
まさかと思いメソッド名を変えたところビューに無事飛んでくれた。
予約語には気をつけよう。

18
23
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
18
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?