[英]Rails polymorphic has_many through association - form broken
In my Rails 5.1 app I am trying to create a tagging system from scratch. 在我的Rails 5.1应用程序中,我试图从头开始创建标记系统。
I want Tags
to be a polymorphic has_many :through
association so that I can tag multiple models. 我希望Tags
是多态的has_many :through
关联,以便可以标记多个模型。
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.last.tags.create(name: "example")
在控制台中创建一个Tag
(以及相关的Tagging
): Note.last.tags.create(name: "example")
生成正确的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. 但是,当尝试通过我的表单创建Tag
及其关联时,该Tag
将不起作用。 I can create the Tag
but no Tagging
. 我可以创建Tag
但不能创建Tagging
。
controllers/notes/tags_controller.rb 控制器/注释/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 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 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 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 标签/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. 错误可能是由于默认需要:tag关联,因此未保存标签。
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. 这不会使tags.name
的唯一性验证失败的tags.name
表明了应用程序中的一个缺点-您应该在数据库中具有唯一索引,并且在模型中应该具有验证以避免重复。
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: 要将现有标签分配给记录,可以使用选择或复选框来传递ID数组:
<%= 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. 要创建新标签,可以使用嵌套属性或使用ajax将POST请求发送到/tags
并更新视图,以使标签最终出现在复选框列表中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.