我在我的Rails 5.1.3应用程序中将Tag
设置为多态模型。我正在尝试设置collection_select
,以便我可以从下拉菜单中选择标记。
表单创建新标签,关联效果很好,但是,我无法将标签名称传递到表单中,因此我的标签将name
保存为空字符串。
_form.html.erb
<%= form_for [taggable, Tag.new] do |f| %>
<%= f.collection_select :taggable_id, Tag.order(:name), :id, :name %>
<%= f.submit %>
<% end %>
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 book_path(@taggable.book_id), notice: "Tag saved"
else
redirect_to root_path, notice: "Sorry, something went wrong"
end
end
private
def tag_params
params.require(:tag).permit(:name)
end
端
Tag params:
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"z2PLrvETqtq742zmr3pghEYYqoGoLv05gLP3OXopLM+blWWw+HmR4AMMDB+5ET3E5YLXeyhMCFMHfdxJNHlkZA==", "tag"=><ActionController::Parameters {"taggable_id"=>"1"} permitted: false>, "commit"=>"Create Tag", "controller"=>"books/tags", "action"=>"create", "book_id"=>"26"} permitted: false>
编辑:添加模型和字段
book.rb
class Book < ApplicationRecord
has_many :tags, as: :taggable
end
tag.rb
class Tag < ApplicationRecord
belongs_to :taggable, polymorphic: true
end
标签表
create_table "tags", force: :cascade do |t|
t.string "taggable_type"
t.integer "taggable_id"
t.integer "user_id"
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
答案 0 :(得分:3)
问题是你正在接近这个问题。
标记只能属于单一资源的标记系统作为分类法几乎毫无价值。相反,您需要与连接表进行多对多关联:
# app/models/tag.rb
class Tag < ApplicationRecord
has_many :taggings
has_many :taggables, through: :taggings
end
# This join model represents a tag attached to a resource
# app/models/tagging.rb
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
# We extract the taggable feature to a concern so that we don't have to repeat it.
# app/models/concerns/taggable.rb
module Taggable
extends ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
end
end
# app/models/book.rb
class Book < ApplicationRecord
include Taggable
end
# just an example
class Film < ApplicationRecord
include Taggable
end
这使用tags
作为规范化表,而不是在表中反复复制名称。这使您可以获得具有特定标签的书籍,而无需使用比使用索引列上的连接慢得多的文本搜索。
要在Taggable
上设置多个标记,您可以使用ActiveRecord为has_many关联创建的tag_ids=
setter:
<%= form_for(@book) do |f| %>
<%= f.text_input :title %>
<%= f.collection_select :tag_ids, Tag.order(:name), :id, :name, multiple: true %>
<%= f.submit %>
<% end %>
这是作为可标记(父)资源的正常创建/更新操作的一部分完成的。可以一次创建单个标记(创建连接记录),但不是很有用。
另一方面, POST /books/:book_id/tags
将用于创建单个标记并设置关联:
<%= form_for([@taggable, @tag]) do |f| %>
<%= f.label :name do %>
<%= f.text_input :name %>
<% end %>
<%= f.submit %>
<% end %>
class TagsController
before_action :set_taggable
# POST /books/:book_id/tags
def create
@tag = @taggable.tag.new(tag_params)
if @tag.save
redirect_to @taggable, success: "New tag created."
else
render :new
end
end
def set_taggable
@taggable = Book.find(params[:book_id])
end
def tag_params
params.require(:tag).permit(:name)
end
end
然而,通过大量使用Ajax,您可以让用户通过发送POST /tags
请求来“内联”创建标记,从而提供更好的用户体验。