active_record 関連テーブルを条件にして検索をする

やっぱりよくわかっていなかったので、メモ。
要は joinsしたテーブルを検索状態にしたいのだけど…というやつです。

目的

Record は Category に紐付いていて、recordの検索の条件に categoryの中にあるカラムを使いたくなりました。
activerecordだけで調べてピンとこなかったので、ここぞとばかりにsqlから学び直すことに。

目的を達成しそうなsql

SELECT "records".*, "categories"."is_payment" 
FROM "records" INNER JOIN "categories"
ON "records"."category_id" = "categories"."id"
WHERE "categories"."is_payment" = false;

するとこんな感じの返しがきた。

id | money  |    date    | card | memo |         created_at         |         updated_at         | category_id | user_id | is_payment
----+--------+------------+------+------+----------------------------+----------------------------+-------------+---------+------------
41 | 290000 | 2016-04-10 | f | | 2016-04-27 09:52:07.124605 | 2016-04-27 09:52:07.124605 | 21 | 1 | f
(1 row)

これをactive recordに変換する

まずjoinsから確認してみた。.to_sqlメソッドを使うと発行されるsqlを見ることができるので便利だった。

$ puts Record.all.joins(:category).to_sql
SELECT "records".* FROM "records" INNER JOIN "categories" ON "categories"."id" = "records"."category_id"
=> nil

意外と発行してもらいたいことと近そうだ。これにwhereをつけていい感じにはきだしてくれたぽい。

$ puts Record.all.joins(:category).where("categories.is_payment = ?", false).to_sql
SELECT "records".* FROM "records" INNER JOIN "categories" ON "categories"."id" = "records"."category_id" WHERE (categories.is_payment = 'f')

結果は

Record.all.joins(:category).where("categories.is_payment = ?", false)
Record Load (0.5ms) SELECT "records".* FROM "records" INNER JOIN "categories" ON "categories"."id" = "records"."category_id" WHERE (categories.is_payment = 'f')
=> [#<Record:0x007fdbbf55a2b0
id: 41,
money: 290000,
date: Thu, 28 Apr 2016,
card: false,
memo: "",
created_at: Thu, 28 Apr 2016 07:02:28 UTC +00:00,
updated_at: Thu, 28 Apr 2016 07:02:28 UTC +00:00,
category_id: 21,
user_id: 1>]

で無事に取れた様子。
where(“categories.is_payment = ?”, false) この部分を文字列じゃない形で渡してあげたい。


追記: 2016/04/28 16:29

と思って調べてみたら、やっぱりあった。
Record.all.joins(:category).where(categories: { is_payment: false} ) という書き方で同等になる模様。


sql書かないと覚えなさそうなので、こんな感じでいろいろ遊んで覚えていきたい。