ham-capのブログ

プログラミング学習の記録

【Rails】findとfind_byとfind_by!についての概要

今回は、おそらくRailsの中でよく見かけるメソッドトップ10に入っているんじゃないかというぐらいよく見る'find'、'find_by'及び'find_by!'について、ぶっちゃけ何が違うのかよくわからんという人向けの記事です。

そもそもこのメソッド達は何なのか?

これら3つのメソッドはActiveRecordによって提供されており、その名のとおりデータベース内から任意のレコードを見つけ出して(取得して)くれる便利なメソッド。

ActiveRecordって何さ?

Railsガイドによると、

Active Recordとは、MVCで言うところのM、つまりモデルに相当するものであり、ビジネスデータとビジネスロジックを表すシステムの階層です。Active Recordは、データベースに恒久的に保存される必要のあるビジネスオブジェクトの作成と利用を円滑に行なえるようにします。Active Recordは、ORM(オブジェクト/リレーショナルマッピング)システムに記述されている「Active Recordパターン」を実装したものであり、このパターンと同じ名前が付けられています。

とのこと。 railsguides.jp

んー。 かなり大雑把に言うとRailsで作られたアプリケーションとデータベースの橋渡し役でありRubySQLの翻訳者、というように個人的には理解しています。

「システムの階層です。」という表現が分かるようで分からない。

データベース周りの便利機能を大量に提供してくれるライブラリみたいなものなのかなと思ったりもしますが、正直このあたりは理解が曖昧です。ライブラリではねーだろゴルァとかありましたら優しく教えてください。

使い方

では使い方です。 全てRailsガイドに基づいて書いていきます。

find

railsguides.jp

findメソッドを使うと、与えられたどのオプションにもマッチする「主キー」に対応するオブジェクトを取り出せます。 findメソッドでマッチするレコードが見つからない場合、ActiveRecord::RecordNotFound例外が発生します。

要は引数に渡した数値とIDが一致するレコードを取得してくれるという理解でいいと思います。

例えば、こんな感じ。

User.find(1) #usersテーブルからIDが1のユーザーを取得。

該当するレコードが見つからない場合は404エラーになります。

find_by

railsguides.jp

find_byメソッドは、与えられた条件にマッチするレコードのうち最初のレコードだけを返します。

findに対してこのfind_byは主キーではない任意のカラムを検索できます。

例えば、

User.find_by(first_name: 'Hoge') #usersテーブルのfirst_nameカラムの値が「Hoge」である最初のレコードを取得。

この場合、対象となるカラムに格納されている各レコードの値が一意である必要はありませんが、気を付けなければならないのはfind_byの場合はfindと違って該当するレコードがない場合の返り値がnilであることです。

もしfind_byがメソッドチェーンの途中にある場合、レコードが見つからずnilが返ってきてしまうと、nilクラスでは続くメソッドが呼び出せずに500エラーになったりします。

そこで次のfind_by!です。

find_by!

find_by! メソッドの動作は、マッチするレコードが見つからない場合にActiveRecord::RecordNotFound例外が発生する点を除いて、find_byメソッドとまったく同じです。

もうこの説明文のとおりです。

レコードが見つからない場合にnilではなく404エラーになります。

気を付けたい点

上記のように、これら3つのメソッドは挙動が微妙に異なります。

それぞれの特徴を把握したうえで、状況によって使い分けられるようになるのが目標です。

個人的には、対象となるレコードのidが分かるのであればfindを使っておけばそんなにおかしなことにはなんやろ、ぐらいの感じで考えています。

それと、大事な点としてこれらのメソッドはどれも、条件に該当するレコードのうち最初の1件だけを取得するメソッドだということです。

findについては主キーが一意なので問題ありませんが、他の2つを使う際に対象となるカラム内の値が一意でない(重複を許容する)場合には注意が必要で、該当するレコードが複数あっても、最初にヒットした1件のみが返るため、他に何件該当するか等は分かりません。

そのため、条件に該当する全レコードを取得したいというような場合は別のメソッドを使用しましょう。

また、レコードが見つからない場合の返り値が例外かnilかというのもポイントですが、何が返ってほしいかはケースバイケースだと思いますので、ひとまずfindfind_by!は例外、find_bynilを返すということを覚えておけば、目的によって使い分けられるかなと思います。

まとめ

というわけで、今回はfindfind_byfind_by!についてでした。

実は僕自身もこの3つの違いについては、先日コードレビューを受けたときにfind_byをメソッドチェーンの中で使っていて、それだとレコードが無い時に次のメソッドが呼び出しエラーになりますよと教えていただいたのがきっかけで初めてちゃんと調べました。

もう少し返り値が何かというのを意識しながらコードを書けるようにならないといけないなと強く思った出来事でした。

では、今回はこの辺で。

記載内容の誤りや事実誤認等に気付かれましたらこっそり教えていただければ幸いです。