In my Rails 5.1 app I am trying to create a tagging system from scratch.
I want Tags
to be a polymorphic has_many :through
association so that I can tag multiple models.
Currently I'm able to create a Tag
(and the associated Tagging
) in the console by doing: Note.last.tags.create(name: "example")
which generates the correct SQL:
Note Load (0.2ms) SELECT "notes".* FROM "notes" ORDER BY "notes"."id" DESC LIMIT $1 [["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.4ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "example"], ["created_at", "2017-10-21 14:41:43.961516"], ["updated_at", "2017-10-21 14:41:43.961516"]]
Note Load (0.3ms) SELECT "notes".* FROM "notes" WHERE "notes"."id" = $1 LIMIT $2 [["id", 4], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "taggings" ("created_at", "updated_at", "tag_id", "taggable_id", "taggable_type") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", "2017-10-21 14:41:43.978286"], ["updated_at", "2017-10-21 14:41:43.978286"], ["tag_id", 9], ["taggable_id", 4], ["taggable_type", "Note"]]
But when trying to create a Tag
and its associations through my form it doesn't work. I can create the Tag
but no Tagging
.
controllers/notes/tags_controller.rb
class Notes::TagsController < TagsController
before_action :set_taggable
private
def set_taggable
@taggable = Note.find(params[:note_id])
end
end
controllers/tags_controller.rb
class TagsController < ApplicationController
before_action :authenticate_user!
def create
@tag = @taggable.tags.new(tag_params)
@tag.user_id = current_user.id
if @tag.save
redirect_to @taggable, success: "New tag created."
else
render :new
end
end
private
def tag_params
params.require(:tag).permit(:name)
end
end
routes.rb
...
resources :notes, except: [:index] do
resources :tags, module: :notes
end
...
.
class Note < ApplicationRecord
belongs_to :notable, polymorphic: true
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
end
class Tag < ApplicationRecord
has_many :taggings
has_many :taggables, through: :taggings
end
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
notes/show.html.erb
<p><%= @note.body %></p>
<%= render partial: 'tags/tags', locals: { taggable: @note } %>
<%= render partial: 'tags/form', locals: { taggable: @note } %>
tags/form.html.erb
<%= simple_form_for [taggable, Tag.new] do |f| %>
<%= f.input :name %>
<%= f.submit %>
<% end %>
The error might be that the Tagging is not getting saved due to the :tag association being required by default.
Try:
class Tagging < ApplicationRecord
belongs_to :tag, required: false
belongs_to :taggable, polymorphic: true
end
Your approach is fundamentially flawed in that it will create duplicates of each tag instead of creating a join record. It also adds unessicary complication in that you have to create nested controllers for each taggable resource.
The fact that this does not fail a uniqueness validation for tags.name
shows a shortcoming in your application - you should have a unique index in the DB and a validation in the model to avoid duplicates.
This would be a perfectly fine approach for something like comments where each created record should be unique but is not for this case where you're linking to an indirect association.
To assign existing tags to a record you can use a select or checkboxes to pass an array of ids:
<%= form_for(@note) do |f| %>
# ...
<%= f.collection_checkboxes(:tags_ids, Tag.all, :id, :name) %>
<% end %>
To create new tags you can use nested attributes or use ajax to send a POST request to /tags
and update the view so that the tag ends up in the list of checkboxes.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.