以下のようなモデルがあります(適当に考えたんですが、SNS的な何かを想定してみましょう)。

1
2
3
4
5
6
7
8
9
10
11
12
class Post < ActiveRecord::Base
  belongs_to :member
end
 
class Member < ActiveRecord::Base
  belongs_to :job
  has_many :posts
end
 
class Job < ActiveRecord::Base
  has_many :members
end

「日付が2010年2月6日で、かつ書いた人の職業名が”Engineer”の人の日記一覧を抽出したい」んですけど、どうしましょう。なお、date属性はPostクラスに、job_name属性はJobクラスにくっついています。

なので、以下では全然ダメです。

1
2
3
4
5
6
Post.find(:all,
  :conditions => {
    :date => Date.new(2010, 2, 6),
    :job_name => "Engineer"
  },
  :include => {:member => :job})

SQL (0.2ms) SET NAMES 'utf8'
SQL (0.6ms) SET SQL_AUTO_IS_NULL=0
Post Load (0.0ms) Mysql::Error: Unknown column 'posts.job_name' in 'where clause': SELECT * FROM `posts` WHERE (`posts`.`date` = '2010-01-31' AND `posts`.`job_name` = 'Engineer')

正解は、

1
2
3
4
5
6
Post.find(:all,
  :conditions => {
    "posts.date" => Date.new(2010, 2, 6),
    "jobs.job_name" => "Engineer"
  },
  :include => {:member => :job})

これで適切なフィールドを条件としたSQLが生成され、正しく抽出できます。

Post Load Including Associations (0.4ms) SELECT `posts`.`id` AS t0_r0, `posts`.`date` AS t0_r1, `posts`.`content` AS t0_r2, `posts`.`member_id` AS t0_r3, `posts`.`created_at` AS t0_r4, `posts`.`updated_at` AS t0_r5, `members`.`id` AS t1_r0, `members`.`name` AS t1_r1, `members`.`job_id` AS t1_r2, `members`.`created_at` AS t1_r3, `members`.`updated_at` AS t1_r4, `jobs`.`id` AS t2_r0, `jobs`.`job_name` AS t2_r1, `jobs`.`created_at` AS t2_r2, `jobs`.`updated_at` AS t2_r3 FROM `posts` LEFT OUTER JOIN `members` ON `members`.id = `posts`.member_id LEFT OUTER JOIN `jobs` ON `jobs`.id = `members`.job_id WHERE (`posts`.`date` = '2010-01-31' AND `jobs`.`job_name` = 'Engineer')

「以下の書き方でダメなの?」 って最初思ったんですが、結論を言うとRails 2.3.5ではOKです。でも、Rails(というかactiverecord)2.2.2ではダメだった。

1
2
3
4
5
6
7
# RAILS_VERSION == 2.3.5
Post.find(:all,
  :conditions => {
    :date => Date.new(2010, 2, 6),
    "jobs.job_name" => "Engineer"
  },
  :include => {:member => :job})

バージョンはなるべく上げたいものです。

おまけ: script/consoleで、ActiveRecordが生成するSQLを見たい

script/consoleに入って、show_sql method to see sql statements in script/console と言うページで紹介されているコードを適切にぶち込めば見られます。下のやつ。