おけらのブログ++

駆け出しWebエンジニアの奮闘記

DBからデータ取得する際にfindとwhereの違いに苦しんだ話

Railsを学び初めて約6ヶ月になりましたが、がっつりRailsを勉強しているわけでもないのでなんとなく使っていて動くけど、ちゃんと理解していない部分がまだまだたくさんあります。
今日はデータベース操作でMysqlからデータを取得する際に、findとwhereの違いについて4時間悩まされた話を書いていきます。

やりたかったこと

Railsを使って、お気に入りのCDアルバムを記録する機能を作っています。
その中で、あるアルバムをお気に入りしている全ユーザー情報を表示したいと思っています。

データベースのイメージ

User n ----- 1 中間テーブル 1 ------ n Album

ハマったこと

まずアルバムの情報が欲しいためあるAlbumインスタンスを取得します。
(既に登録されている前提)

# アルバム名とアーティスト名が一致するアルバムのインスタンスを取得
 @albm = Album.where(album: album, artist: artist)

# そのアルバムをお気に入りしているユーザーのインスタンスを取得
 @users = @albm.favorite_users

どう見てもこれでOKだろうと自信満々に保存すると次のエラーに怒られました。

undefined method `favorite_users' for #

要は@albmはfavorite_usersメソッドなんて持ってないぞ!と言っています。その原因は調べても調べてもわかりませんでした。
そしてrails consoleを使って「find」は上手くいくけど、「where」だと上手くいかないという事実を漸く見つけ解決に繋がりました。

findはインスタンスを返すが、whereは範囲を絞るのみ

find (find_by)はすぐに利用できるデータ

findは指定したモデルのインスタンス(すぐに利用できるデータ)を返す

特定のIDのデータを取得したい場合はfindやfind_byを使いますが、これらはある特定(一意)のインスタンスを取得します。そのため先の例でいえば@albm.idのように使えます。

whereは範囲を絞っただけ

whereメソッドを使った場合、指定したモデルのインスタンスではないのでそのままデータを利用できない

where句とはあくまで条件に当てはまるインスタンスの集合体であり、一意のインスタンスを取得しているわけではないのです。今回の場合、一つしかないことが分かっていたため頭では一意に絞ったものだと思いこんでいました。そのため@albm.idがエラーとなってしまっていました。

解決方法

# アルバム名とアーティスト名が一致するアルバムのインスタンスを取得
 @albm = Album.where(album: album, artist: artist).first

とすることで解決しましたが、ブログを書きながら下記でもできることが分かったのでfind_byを使うことにしました。

# アルバム名とアーティスト名が一致するアルバムのインスタンスを取得
 @albm = Album.find_by(album: album, artist: artist)

以上、おそらく初心者が陥りやすそうなfindとwhereの違いでした。

参考サイト
qiita.com