Railsでのいいね機能の実装ではまったところ findとfind_byの違い
今日は、画像投稿アプリのいいね機能を実装していました。
User,Postとのアソシエーションは以下のように1対nになっています。
User 1-n Like n-1 Post
したがって、likesテーブルには、user_idとpost_idの組み合わせがレコードとして挿入されるような実装になります。 Likeテーブルのcreateメソッドで正しい値がはいらず苦戦しました。気づいてみれば単純なことですが、findとfind_byの仕様の違いでした。
以下のような実装をしていました。
実装内容
_like.html.slim
= link_to likes_path(post_id: post.id), method: :post, remote: true do = icon 'far', 'heart', class: 'fa-lg'
likes_controller.rb
class LikesController < ApplicationController def create binding.pry @post = Post.find(params[:post_id]) current_user.like(@post) end end
※ likeというUserモデル内のメソッドでLikeテーブルに挿入するようにしています。
確認したこと
post_id
が2のPostについて、いいねのボタンを押して実行したところ、railsサーバーのコンソール表示では以下のようなレコードが挿入されました。
Like Create (3.3ms) INSERT INTO `likes` (`user_id`, `post_id`, `created_at`, `updated_at`) VALUES (2, 1, '2019-10-27 19:07:58', '2019-10-27 19:07:58')
post_id
が2のPostにLikeをしたはずなのに、ずれた値が挿入されています。
よくよくソースをみかえしたところ、controllerで
@post = Post.find_by(params[:post_id])
の部分を
@post = Post.find(params[:post_id])
にしなければいけないことに気づきました。
上記のInsert分の前にfindしている部分のSQLのSELECT文をみると
Post Load (2.0ms) SELECT `posts`.* FROM `posts` WHERE (2) LIMIT 1
のようになっています。これはエラーにならずにWHERE句の文がtrueになって一番最初のPostが返却されるようになっていました(だから post_id
が1)
find_byの部分をfindに変えたところ、以下のように変化して、正常にInsertされるのが確認できました。
Post Load (6.9ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
「findはidを検索するもの、find_byは引数を指定して検索するもの」 という理解はしていたのですが、いろいろ試行錯誤しているうちにfind_byのままにしてしまったようです。
間違えた場合に、エラー制御してくれると暗黙的に期待していたようで、気づくのに時間がかかりました。アソシエーションがやや複雑でidが複数あるので、それを整理するのを先にやったほうがよかったかもしれません。
テストを先に書くようにするか、そうでなければ発行されるSQLをよくよく観察するという習慣が必要だと感じました。
(所要時間 45分)