How To Access Has_many Through Relationship Possible In Rails?

How can I access my related records?

class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories

class Categories < ActiveRecord::Base
has_many :post_categories
has_many :post, through: :post_categories

class PostCategories < ActiveRecord::Base
belongs_to :post
belongs_to :category

PostCategories table has id, posts_id, and categories_id columns.

id | posts_id | categories_id
1. | 2       | 3
2. | 2       | 4

What I want is: to get posts related to a category. like: all Postswhere in x category.



Yep, this is an easy one.

one_or_more_categories = # Category.find... or Category.where...
posts = Post.joins(:categories).where(category: one_or_more_categories)

Rails is clever enough to take either a model or a query that would find some data and turn that into an efficient appropriate query, that might be a subquery. Trying things out in the Rails console (bundle exec rails c) is a good way to see the generated SQL and better understand what's going on.

(EDIT: As another answer points out, if you've already retrieved a specific Category instance then you can just reference category.posts and work with that relationship directly, including chaining in .order, .limit and so-on).

Another way to write it 'lower level' would be:

Post.joins(:categories).where(category: {id: one_or_more_category_ids})

...which is in essence what Rails will be doing under the hood when given an ActiveRecord model instance or an ActiveRecord::Relation. If you already knew the e.g. category "name", or some other indexed text column that you could search on, then you'd adjust the above accordingly:

Post.joins(:categories).where(category: {name: name_of_category})

The pattern of joins and where taking a Hash where the join table name is used as a key with values nested under there can be taken as deep as you like (e.g. if categories had-many subcategories) and you can find more about that in Rails Guides or appropriate web searches. The only gotcha is the tortuous singular/plural stuff, which Rails uses to try and make things more "English-y" but sometimes - as in this case - just creates an additional cognitive burden of needing to remember which parts should be singular and which plural.