Search code examples
ruby-on-railsfilterassociationshas-and-belongs-to-many

How to filter by associated attributes in a has_and_belongs_to_many association


I have two models, articles and tags, with a has_and_belongs_to_many relationship. There is a simple join table with two columns, article_id and tag_id, with no index. In my articles index template I want to be able to filter on specific tags using a select box where you select the tag.name field and this is put in the url as a query sting and the controller filters the articles by that tag. Below is my setup and it throws the error "SQLite3::SQLException: no such column: articles.tag_id". It is correctly adding ?tag=name to the url and the controller is correctly assigning @tag but it fails from there. How do I get this to work?

Models

# app/models/article.rb
has_and_belongs_to_many :tags

#app/models/tag.rb
has_and_belongs_to_many :articles

Controller

# app/controllers/articles_controller.rb
def index
  if params[:tag]
    @tag = Tag.find_by(name: params[:tag])
    @articles = Article.where(tag_id: @tag.id)
  else
    @articles = Article.all
  end
end

View

# app/views/articles/index.html.erb
<%= form_tag(articles_path, method: "get") do %>
  <%= select_tag "tag", options_from_collection_for_select(Tag.all, "name"),
       prompt: "Select Tag" %>
  <%= submit_tag "Submit" %>
<% end %>

Solution

  • # app/views/articles/index.html.erb
    <%= form_tag(articles_path, method: "get") do %>
      <%= select_tag "tag_ids", options_from_collection_for_select(Tag.all, :id, :name),
           prompt: "Select Tag", multiple: true %>
      <%= submit_tag "Submit" %>
    <% end %>
    
    # app/controllers/articles_controller.rb
    def index
      if params[:tag_ids]
        @articles = Article.joins(:tags).where('tags.id' =>  params[:tag_ids])
      else
        @articles = Article.all
      end
    end
    

    See Active Record Query Interface - Specifying Conditions on the Joined Tables