简体   繁体   English

Rails多态has_many通过关联-表单损坏

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM