0
votes

In rails 4 I have a many to many relationship with an intermediate table in the database and has_and_belongs_to_many in each model referring to each other

The relationship looks like this...

tags >--- posts_tags ---< posts

right now I have an html form that a user uses to create a new post and the id's of their chosen tags are getting uploaded as an array named tags[]

My question is whats the best way to add all these posts_tags to the database? I realize I can just foreach through the tag id's and use Tag.find to retrieve those models but that will be alot slower than a single insert query that inserts the post id and each tag id into the post_tags column for each tag added. I also would prefer to do this the active record way to keep things short and clean.

2

2 Answers

1
votes

Collection

Rails has a special piece of functionality which allows you to populate associative collections by using collection_ids. I can't find any documenation on this, but it's there because we've used it before:

#app/controllers/posts_controller.rb
class PostsController < ApplicationController
    def create
        @post = Post.new post_params
        @post.save
    end

    private

    def post_params
        params.require(:post).permit(:tag_ids)
    end
end

This means if you pass any value into the tag_ids attribute, Rails will save the collection for you. You don't need to do accepts_nested_attributes_for or anything - it just works

By using @Baloo's select element with this backend should allow you to populate the post_tags

--

Individual

If you wanted to add these items individually, you'll want to use either << or .delete methods in your controller:

#app/controllers/posts_controller.rb
Class PostsController < ApplicationController
   def change_tag
       @post = Post.find params[:id]
       @tag = Tag.find params[:tag_id]

       @post.tags << @tag if request.post?
       @post.tags.delete(@tag) if request.delete?
   end
end
1
votes

The association itself will give you methods you can use. In your association a post will have a tag_ids method which you can give an array of tag ids. This means you can do this:

<%= form_for @post do |f| %>
  <div class="field">
    <%= f.label :tag_ids %><br />
    <%= f.select :tag_ids, Tag.order(:name).pluck(:name, :id), {}, :multiple => true, :size => 6 %>
  </div>
<% end %>