I would like to implement custom tagging feature. So far I have Item and Tag models but I struggle with submission of tags – Rails keep telling me that Tag assignment is invalid whenever I try to create new Item.
(Creation of new item represents text area for item description and bunch of checkboxes with all existing tags from db.)
Curiously updating of tags of existing items works fine. This observation led me to discovery that culprit lies in fact that during build the item object is not persisted so it does not have an id yet, hence the relationship between item and tags cannot be established at the moment.
Based on this I have augmented create action in ItemsController with following contraption.
While this works it seems like ugly hack to me, so I would like to know what is the proper way™ to handle this situation.
class ItemsController < ApplicationController
⋮
def create
tags = {}
tags[:tag_ids] = item_params.delete('tag_ids')
reduced_params = item_params.reject{|k,v| k == 'tag_ids'}
@item = current_user.items.build(reduced_params)
@item.save
@item.update_attributes(tags)
Relevant code
class Item < ApplicationRecord
has_many :tag_assignments, foreign_key: 'tagged_item_id'
has_many :tags, through: :tag_assignments
⋮
class TagAssignment < ApplicationRecord
belongs_to :tagged_item, class_name: 'Item'
belongs_to :tag
⋮
class Tag < ApplicationRecord
has_many :tag_assignments
has_many :items, through: :tag_assignments
⋮
items_controller.rb
def create
@item = current_user.items.build(item_params)
⋮
private
def item_params
params.require(:item).permit(:description, :tag_ids => [])
end
_item_form.html.erb
⋮
<section class="tag_list">
<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name do |cb| %>
<% cb.label {cb.check_box + cb.text} %>
<% end %>
</section>
I would recommend using the acts_as_taggable_on gem. It solves almost all situations regarding the use of tags.