更新日:
【Rails】 世界で一番詳しいfind_byメソッドのいろいろな使い方
find_byメソッドとは、検索でヒットしたレコードの初めの一件だけを返すメソッドです。
1
モデル名.find_by(条件)
例えば、以下のコードを実行すると、usersテーブルから「ageカラムの値が25」という条件に合う最初のレコードを1件だけ返します。
1
2
User.find_by(age: 25)
SELECT `users`.* FROM `users` WHERE `users`.`age` = 25 LIMIT 1
このように、find_byメソッドの引数に渡した条件に一致した最初のレコードを1件だけ返します。
find_byメソッドの使い方
この章では、find_byメソッドの使い方について1つ1つ丁寧に解説します。
find_byメソッドを使用する場面とは?
検索したいカラムに対して値をセットして実行すると、Userモデルのインスタンスが一件だけ返ってきます。
1
2
3
4
5
6
7
8
9
10
11
User.find_by(email: 'programan@gmail.com')
=> #<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>
find_byを使う箇所は下記の3点あります。
- 一件しかヒットしないレコードに対して使用
- unique制約がかかっているカラムに対して使用
- 完全一致以外での検索に使用(あいまい検索等、完全一致よりも使用頻度は少ない)
find_byは一件だけしか返ってこないので、単一カラムの検索であればunique制約をかけているカラムの検索に使うことが多いです。
あいまい検索
programanという文字が含まれている中で、誰でも良いからとりあえず一人のデータを見たいなという場合
1
2
3
4
5
6
7
8
9
10
11
User.find_by("name like '%programan%'")
=> #<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>
programanという文字が含まれているユーザーは他にもいますが、find_byでは複数件ヒットした中の初めの一件だけ返ってきてます。
不等号検索
あれ、今日ユーザー登録してくれたユーザーいたかな??という時にはこんな感じで検索します。
1
2
3
4
5
6
7
8
9
10
11
User.find_by('created_at > ?', Date.today)
=> #<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>
これでデータ返ってきてるんで、あ、今日ユーザー登録してくれた人いるな!という感じです。
もし登録してくれた人がいなかった場合はnilが返ってきます。
1
2
3
User.find_by('created_at > ?', Date.tomorrow)
=> nil
nilが返ってきたら登録してくれたユーザーはいなかったなと確かめられる訳です。
複数の条件で指定した場合
そもそも一件しかヒットしないレコードに対してfind_byを使うことが多いと言いましたが、それは複数の条件を指定した場合でも同じです。それが複合uniqueのカラムに対しての検索です。複合uniqueとは一つのカラムの場合はいっぱいデータあるけど、複数カラムの一致するデータは一件しかないよという場合です。
例えば今みなさんに見てもらっているピカわか!を例に考えてみましょう。
ピカわか!では、カテゴリー(ruby rails javascript等)ごとに記事が書かれています。
ピカわか!のblogsからifという記事を取り出したいときに
rubyにもifという記事はありますし、javascriptにもifという記事があります。
すると、下記のように先に作成されたrubyのifの記事が取得されます。
javascriptのifの記事は取得されません。(find_byはヒットしたレコードのうち初めの一件を返すメソッドであるからです。)
1
2
3
4
5
6
7
Blog.find_by(url_title: 'if')
=> #<Blog:0x007ff9172b9ba8
id: 172,
title: "if文を使っての条件分岐を徹底解説!",
url_title: "if",
category_id: 2(rubyのカテゴリー)
Blog.whereで複数出てくるか確認してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Blog.where(url_title: 'if')
=> [#<Blog:0x007ff9172cc4d8
id: 172,
title: "if文を使っての条件分岐を徹底解説!",
url_title: "if",
category_id: 2(rubyのカテゴリー),
#<Blog:0x007ff9172cc398
id: 1343,
title: "javascriptのif",
url_title: "if",
panel_name: nil,
panel_type_id: 52,
category_id: 42(javascriptのカテゴリー)>]
上記のようにifに関して2つのレコードが取得されてしまいます。rubyのifの記事かjavascriptのifの記事かどちらか一つの記事を表示しなければいけないので、category_idとurl_titleを指定してそれぞれどのカテゴリーのif文のレコードを取得するように書く必要があります。
1
2
3
4
5
6
7
Blog.find_by(category_id: 2, url_title: 'if')
=> #<Blog:0x007ff9172b9ba8
id: 172,
title: "if文を使っての条件分岐を徹底解説!",
url_title: "if",
category_id: 2(rubyのカテゴリー)
1
2
3
4
5
6
7
8
9
Blog.find_by(category_id: 42, url_title: 'if')
<Blog:0x007ff9172cc398
id: 1343,
title: "javascriptのif",
url_title: "if",
panel_name: nil,
panel_type_id: 52,
category_id: 42(javascriptのカテゴリー)>
それぞれurl_titleをifでfind_byした場合には、どのカテゴリーのif文が取得できるかわからないが、category_idとurl_titleを指定することで想定した一件のblogを取得したい!このようなケースでfind_byを使うことがよくあります。
アソシエーションに対して使用する場合
単体モデルで使いがちですが、アソシエーションに対してfind_byを使うことも良くあります。
このアソシエーションで使うケースはよくあるのですが、例えばbefore_action
でset_category
みたいなメソッドを実行していて@category
がある場合に使うことがあります。
イメージとしては、下記の様な形で使うことがあります。
1
2
3
4
5
6
7
8
9
before_action :set_category
def show
@blog = @category.blogs.find_by(url_title: 'if')
end
def set_category
@category = Category.find(params[:category_id])
end
このparams[:category_id]に2(rubyのカテゴリー)が入っていたら、@categoryにはjavascriptのcategoryのインスタンスが入っているので、javascriptのifの記事が@blogに入る形です。
アソシエーションについては、「アソシエーションを図解形式で理解する」を参考にしてください。
カラムが存在しない、ヒットしたものがない、例外を発生させたい場合
find_byメソッドを実行して該当のデータが無かったり、カラム名を間違えていたり、例外を発生させたい場合、どのように記述すれば良いのでしょうか?
検索にヒットしたものがなかった場合
find_byでヒットしたものがなかった場合はnilが返ってきます。
1
2
3
Blog.find_by(url_title: 'ifhogehoge')
=> nil
該当のカラムが存在しない場合
該当のカラムが存在しない場合は、エラーが返ってきます。
1
2
Blog.find_by(url_titleaaa: 'if')
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'blogs.url_titleaaa' in 'where clause'
例外を発生させたい場合
レコードが絶対見つかる場合を想定しているのに、万が一レコードが見つからない場合は想定外のrequestを送ってきていると考えられ、例外処理でエラーページを表示したい時があるとします。
そういうときはfind_byではなく、find_by!を使います。
通常のfind_byだとヒットしたものがなかった場合はnilが返ってくるが、find_by!を使うと例外を発生させられることが出来ます。
1
2
3
Blog.find_by!(url_title: 'hogehoge')
ActiveRecord::RecordNotFound: Couldn't find Blog
他のメソッドとの違い
この章では、findメソッドやwhereメソッドとの違いについて解説します。
findメソッドとの違い
find_byメソッドはid以外の検索で使用するのに対し、findメソッドはidのみしか検索できません。返り値は単数ならインスタンスが返ってきます。
1
2
3
4
5
6
7
8
9
10
11
User.find(1)
=> #<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>
id以外で検索した場合
もしid以外で検索するとエラーが出てしまいます。
1
2
3
User.find(name: 1)
ActiveRecord::RecordNotFound: Couldn't find User with 'id'={:name=>1}
配列で指定した場合
単数ならインスタンスで返ってきますが、配列の場合だと配列で返ってきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
User.find([1,2])
=> [#<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>,
id: 2,
email: "programan_father@gmail.com",
name: "programan_father",
job_id: 2,
sex: 0,
age: 58,
tall: 173.0,
weight: 60.0,
created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]
find_byでid検索した場合
1
2
3
4
5
6
7
8
9
10
11
User.find_by(id: 1)
=> #<User:0x007fe080f0ecf0
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25, tall: 175.0, weight: 68.0,
created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>
インスタンスが返ってきます。findと一緒です。ただfindはid検索専用のメソッドなので、わざわざfind_byを使うことはないです。基本的にidの検索の場合はfindメソッドを使用して、id以外の検索の場合でuniqueなレコードを取得したい際にfind_byメソッドを使用します。
findメソッドの詳しい具体的な使い方はfindメソッドを徹底解説をご覧ください。
whereメソッドとの違い
find_byメソッドとwhereメソッドの一番の違いは、find_byメソッドの返り値がインスタンスであるのに対し、whereメソッドの返り値は配列であるということです。返り値がどう違うか見ましょう。
find_byメソッドとの返り値の違い
1
2
3
4
5
6
7
8
9
10
11
12
13
User.where(name: 'programan')
=> [#<User:0x007fe07e36c480
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25,
tall: 175.0,
weight: 68.0,
created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]
一件しかヒットしなくても配列が返ってきます。
メソッドチェーンでfind_byメソッドを使う場合
whereの返り値に対してチェーンしてfind_byメソッドを書くことも出来ます。
その場合はand検索になります。
下記のサンプルコードの場合だとnameカラムがprogramanであるかつageが25のレコードを一件取得するというふうになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
User.where(name: 'programan').find_by(age: 25)
=> [#<User:0x007fe07e36c480
id: 1,
email: "programan@gmail.com",
name: "programan",
job_id: 1,
sex: 0,
age: 25,
tall: 175.0,
weight: 68.0,
created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]
whereメソッドの詳しい具体的な使い方はwhereメソッドを徹底解説をご覧ください。
この記事のまとめ
- find_byメソッドは、検索条件にヒットした初めの一件を返すメソッドのこと
- uniqueなカラムや複合uniqueなカラムに対して使用する