Railsでアソシエーションされたモデルを条件にして検索する
ブログポストが複数カテゴリを持てるという場合で、
ある記事と同じカテゴリを持つ記事を探すということをやりたい。
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)