Search code examples
ruby-on-railsrubyruby-on-rails-5ruby-on-rails-6

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 Posts where in x category.


Solution

  • 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.