🎱

railsの関連付け・外部キーでハマった結果、deviseの奥深さ…というか基礎を学んだ話

2022/10/06に公開約2,700字

はじめまして、こういった記事を書くのは初めてになります。
おてやわらかにお願いします!

記事の内容・前提

  • ruby2.7, rails6.0で個人学習用にアプリを開発中
  • userモデル(devise)とroomモデルを関連付けたのに、外部キーroom_idに値が入らない
  • 問題を解決したところ、知らなかったdeviseの仕様もついでに学べた

トラブっていたこと

userモデル(devise)の外部キーroom_idに値が入らない。

実現させたいのは、userがroomを作るとき、userの外部キーroom_idに、作ったroomのidを入れること。

/models/user.rb

  belongs_to :room, optional: true

 validates :name, presence: true, length: { in: 2..40 }
 validates :password, presence: true, length: { in: 6..30 }
 validates :email, ...(略)

/models/room.rb

 has_many :users

モデル間の関連付けは問題ないはず…

/controllers/rooms_controller.rb

def create
  @room = Room.new(room_params)
  if @room.save
    @user.update(room_id: @room.id)
    redirect_to room_url(token: @room.token)
  else
    redirect_to root_path
  end
end

4行目の@user.update(room_id: @room.id)で外部キーにidが入りそうなのに結果は×。
ログを見ると

 ↳ app/controllers/rooms_controller.rb:18:in `create'
   (0.6ms)  ROLLBACK

ロールバック!?
そんな悪いことしたでしょうか(汗)

ロールバックの原因

先に原因をお伝えします。
原因は「userモデルで設定されているバリデーションが発動したから」でした。

解決した方法

まず、先ほどの@user.update(room_id: @room.id)
4行目の@user.update!(room_id: @room.id)に変更しました。

ロールバックの心当たりがなかったので、update!にして、エラー内容を把握しようと考えたからです。

ビックリをつけると(updateからupdate!)、何が変わるのか?
以下の記事から学ばせていただきました。引用します

メソッドを実行した結果、バリデーションに失敗したとき(DBに保存できなかったとき)に例外を発生させる。
Ruby や Rails でよく見る、メソッド名の後ろのびっくりマーク(感嘆符)の意味

話を戻します。

update!で試したところ、エラーの表示が!
内容は…

「…バリデーション?パスワード?どゆこと???」

とりあえず、userモデルのpasswordのバリデーションをコメントアウトしました。

/models/user.rb

 # validates :password, presence: true, length: { in: 6..30 }

再度update!で試すと…

エラーなく通りました。

なんで?

(このあと、原因を考えたのですが、userモデルにpasswordカラムがないので(代わりにencrypted_passwordカラムがある)、user.rbにpasswordのバリデーションがあるのはおかしいから、だと見当をつけました)

・・・

ひとまず、当初の問題、「userモデル(devise)の外部キーroom_idに値が入らない」は打開策が見つかりました。

生じた疑問とその答え

当初の問題はクリアできました。

でも新たな疑問が。

さっきpasswordのバリデーションをコメントアウトしたわけですが、そのままuserの登録をパスワード3文字で試してみると…

なんで???

deviseはどこでpasswordのバリデーションをしてるのか?

完全に疑問です。

・・・

ググったらすぐにわかりました。引用します。

deviseの場合、パスワードはデフォルトで「6〜128文字」で制限がかけられています。
こちらは、「config/initializer/devise.rb」にて変更できます
Railsのdeviseでモデル作成後に実行したい3つのバリデーションは?

なるほど…。

そもそもdeviseはここでpasswordのバリデーションを設定しているんですね。あとemailも。全然知りませんでした。

つまり、冒頭に/models/user.rbのコードを載せましたが、
/models/user.rb

 validates :name, presence: true, length: { in: 2..40 }
 validates :password, presence: true, length: { in: 6..30 } #いらない
 validates :email, ...(略) #いらない

ということですねおそらく。おそらくですが。

今回の学び

  • deviseで作ったモデルのemailとpasswordにはconfig/initializer/devise.rbデフォルトでバリデーションが設定されている

Discussion

ログインするとコメントできます