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>
You might want to use virtual attributes, in this case tag_list , which will use either new or already created and persisted tags present in the database.
Now in your items form all you need from tags is to just refer the virtual attribute instead of
This should be simple and should result in less complicated code overall. Also do you not forget to change your current code accordingly. For example, in your items controller
Now your item new and create actions can be as simple as