Railsでアソシエーションされたモデルを条件にして検索する

Shunsuke Sawada

ブログポストが複数カテゴリを持てるという場合で、
ある記事と同じカテゴリを持つ記事を探すということをやりたい。
 

Screen Shot 2014-02-23 at 9.34.23 AM
 

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
#Postモデル
class Post < ActiveRecord::Base
    has_and_belongs_to_many :categories
end

#交差テーブル
class CategoriesPost < ActiveRecord::Base
end

#Categoryモデル
class Category < ActiveRecord::Base
    has_and_belongs_to_many :posts
end

 

ruby
1
2
3
4
5
post = Post.first
categories = Category.joins(:posts).where("posts.id = ?", post.id).select("categories.id")
category_ids = categories.map { |c| c.id }.join(',')

posts = Post.joins(:categories).where("categories.id IN (#{category_ids})").uniq

 
これで取り出せはするんだけど、カテゴリは複数持てる設定だから、INでやると当然記事は重複する。id=1の記事はカテゴリ13,33,36を持っているからid=1の記事を3回も取得してしまう。

uniqで重複は消してしまえるけど、もっとスマートな方法ないかな。
SQLに詳しい人教えてください。


references(*args)という便利なメソッドを@naotorさんから教えて頂きましたー。
Categoryのidは自分で集めなくてもpost.category_idsでいけるんですね。
いつかRailsチュートリアルで見た気がするな。

http://api.rubyonrails.orgから引用

ruby
1
2
3
4
5
6
7
User.includes(:posts).where("posts.name = 'foo'")
# => Doesn't JOIN the posts table, resulting in an error.

User.includes(:posts).where("posts.name = 'foo'").references(:posts)
# => Query now knows the string references posts, so adds a JOIN

ruby
1
2
post = Post.first
posts = Post.includes(:categories).where(categories_posts: {category_id: post.category_ids}).references(:categories)
128
Shunsuke Sawada

おすすめの記事

acts-as-taggable-on タグを表示させる順番を決めたい
Railsを4.2にバージョンアップしたら、Vagrantのローカル開発環境にアクセスできなくなった問題
Railsのバリデーションエラー後にレイアウトが崩れるとき