Search code examples
ruby-on-railsformshas-and-belongs-to-manycollection-select

Option from collection_select creates a new one on submit - Rails 5


Today I've been working on a HABTM association between my Plugins and Categories. I got it almost working, but run into trouble with the collection_select.

I have a select in my form and I succesfully call all the existing Categories, but when I submit the form, a new Category is created. For example I select the category Synthesizer. When I submit, I suddenly have two categories called Synthesizer. How can I make it so that the Plugin is associated with the Category, but does not create a new one?

Here is the code from my form:

<%= f.fields_for :categories do |c| %>
  <%= c.label :name %>
  <%= c.collection_select :name, Category.order(:name), :name, :name, multiple: true, include_blank: true %>
<% end %>

This is how I've set my strong params:

def plugin_params
  params.require(:plugin).permit(:name, :url, :image, :description, :categories_attributes => [:id, :name])
end

And in my Plugin model:

has_and_belongs_to_many :categories
accepts_nested_attributes_for :categories

If you miss context, please let me know. Thanks a lot in advance for your help! :)


Solution

  • A really common newbie misconception is that you need nested attributes to assign associations. Nested attributes is used to create brand new categories (or edit existing ones) from the same form as the plugin and is usually best avoided.

    Remember here that there is huge difference between categories and rows on the categories_plugins join table. You want to create the the later.

    All you really need to to do is use the _ids setter / getter created by has_and_belongs_to_many.

    class Plugin
      has_and_belongs_to_many :categories
    end
    
    <%= form_with(model: @plugin) do |f| %>
      # ...
      <%= c.collection_select :category_ids, Category.order(:name), :id, :name, multiple: true, include_blank: true %>
      # ...
    <% end %>
    
    def plugin_params
      params.require(:plugin).permit(:name, :url, :image, :description, category_ids: [])
    end
    

    The category_ids= setter will handle inserting/deleting rows into the categories_plugins join table automatically.