【Rails5】`find`と`find_by`の違い
単一のレコードを取得する2つのメソッドの違いをまとめました. これについての記事はいくらでもあるけどね.
違い
find
id
で検索を行う.- レコードが見つからない場合はエラー.
User.find(1000) ActiveRecord::RecordNotFound (Couldn't find User with 'id'=10)
nil
を渡した場合も.
User.find(nil) ActiveRecord::RecordNotFound (Couldn't find User without an ID)
find_by
id
以外でも検索可能.
User.find_by(email: 'hello@world.com')
- レコードが見つからない場合は
nil
を返す.
User.find_by(id: 1000) #=> nil
nil
を渡した場合はIS NULL
で検索.
User.find_by(id: nil) User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IS NULL LIMIT 1
使い分け
404エラーを表示させたい場合はfind
例えばユーザーの詳細ページ(プロフィールページ)を考えてみましょう. コントローラでは以下のようにインスタンス変数を定義するでしょう.
def show @user = User.find(params[:id]) end
もしexample.com/users/1000
のように, 存在しないユーザーを表示させようとすると, 404エラーページを表示してくれます.
ここでfind_by
を使ってしまうと, @user = nil
となり, view側でエラーが発生してしまいますね.
nil
を期待する場合はfind_by
エラーを発生させたくない, そしてレコードがなければnil
を期待するという場合はfind_by
を使います. 例えばRails Tutorialで定義されているcurrent_user
ヘルパーメソッドの定義が以下です(ただし8章時点でのもの).
def current_user @current_user ||= User.find_by(session[:user_id]) end
ここでfind
を使うと, エラーが生じてしまい大変ですね.
id
以外で検索したいが, エラーを発生させたい場合
この場合はfind_by!
が用意されています. この記事を投稿して2日後に, ちょうどこのメソッドを使う機会があったので追記します.
例としてPost
という架空のモデルを考えます. Post
モデルはdraft
というboolean
型のattributeを持っているとします.
# schema.rb の省略版 create_table "post" do |t| t.string "title" t.text "body" t.boolean "draft" end
draft: true
によって下書きとしてPost
を保存できます. 他人は下書きにアクセスできないようにしたいとします. こんなときは次のように実装すればよさそうです.
class PostsController < ApplicationController def show @post = Post.find_by!(id: params[:id], draft: false) end end
下書き保存されたPost
にアクセスすると, まるでレコードが存在しないかのように振舞ってくれますね.