在 Rails 中手动添加 find_or_create 标签。标签已创建但未找到 ID
In Rails to manually find_or_create tags. Tags create but id not found
我有一个联系人控制器和一个更新操作。我已经实现了标记 s.t。它适用于现有标签。在新标签的实例中,我想创建它然后添加它。我可能很难做到这一点。目前,当输入新标签时,我收到错误消息:ActiveRecord::RecordNotFound (Couldn't find all Tags with 'id': (3, 6, 1, 0) (found 3 results, but was looking for 4). Couldn't find Tag with id 0.):
刷新页面后,我可以看到标签已经存在,可以正常添加了。
这是我的控制器更新操作:
def update
respond_to do |format|
tagIds = []
params[:contact][:tag_ids].each do |tagName|
if tagName != '' && Tag.where(:name => tagName).blank?
Tag.create!(:name => tagName)
end
tagIds.push(Tag.where(:name => tagName).ids)
end
puts "contact_params is: #{contact_params}"
contact_params[:tag_ids] = tagIds
if @contact.update(contact_params)
format.html { redirect_to @contact, notice: 'Contact was successfully updated.' }
format.json { render :show, status: :ok, location: @contact }
else
format.html { render :edit }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
联系人参数 returns:{"name"=>"john smith", "image_url"=>"", "website"=>"", "bio"=>"", "phone"=>"", "email"=>"someone@gail.com", "location_info"=>"", "tag_ids"=>["", "3", "6", "1", "CBD"], "category_ids"=>[""]}
参数是:
{“utf8”=>“✓”,“authenticity_token”=>“eIwSn9RGzMdds5jrewOvu1VT6ECadlRPCQa6CASQ7VhKgrqVtAYa7FKlVqk5t8IFQVv2b9yA5ruaa0NuT7DxwQ==”,“联系人”=>{“名称”=>“约翰·史密斯”,“[=>32=]” "", "email"=>"someone@gail.com", "image_url"=>"", "网站"=>"", "location_info"=>"", "bio"=>"", "tag_ids"=>["", "3", "6", "1", "CBD"], "category_ids"=>["" ]}, "提交"=>"更新联系人", "id"=>"1"}
Rails 方法 将使用 nested attributes 而不是将现有记录的 ID 与新标签混合在一起:
class Contact < ApplicationRecord
has_many :taggings
has_many :tags, though: :taggings
accepts_nested_attributes_for :tags
end
class Tag < ApplicationRecord
has_many :taggings
has_many :contacts, though: :taggings
validates_uniqueness_of :name
end
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :contact
end
您可以通过以下方式使用现有标签和新标签创建 contact/update:
Contact.create(
name: 'Bob',
tag_ids: [1, 2, 3],
tags_attributes: [
{ name: "foo" }, { name: "bar" }, { name: "baz" }
]
)
这不是唯一的方法,从用户体验的角度来看,这通常不是最佳解决方案,因为他们必须提交表单,然后发现标签已经存在,而且它也很容易创建一堆重复项/ 拼写错误。
一个好的替代方法是使用自动完成查询 TagsController#index
以在用户键入时按名称搜索标签以查找现有标签(如在 Whosebug 上看到的!)以及 ajax 调用如果标签不存在,则创建一个单独的 TagsController#create
方法。然后,通过向 select/checkbox 或隐藏输入添加选项,将返回的 id 添加到标签列表(tag_ids
参数)。
这会为用户提供即时有用的反馈,将关注点分离到后端并降低复杂性。
如果你想挽救你的方法,你可以通过:
class Contact < ApplicationRecord
# ...
accepts_nested_attributes_for :tags
end
class ContactsController < ApplicationController
def update
contact_params.merge!(
separate_tags(params[:contact][:tag_ids])
)
respond_to do |format|
if @contact.update(contact_params)
format.html { redirect_to @contact, notice: 'Contact was successfully updated.' }
format.json { render :show, status: :ok, location: @contact }
else
format.html { render :edit }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
private
def separate_tags(ids)
tags = Tag.where(tag_ids).ids.to_set
ids, names = tag_ids.separate do |id|
tags.include?(id)
end
{
tag_ids: ids,
tags_attributes: [
names.map {|name| { name: name }}
]
}
end
end
然而,使用混合了 ID 和名称以及 YMMV 的单个数组感觉像是一种非常 hacky 的方法。
我有一个联系人控制器和一个更新操作。我已经实现了标记 s.t。它适用于现有标签。在新标签的实例中,我想创建它然后添加它。我可能很难做到这一点。目前,当输入新标签时,我收到错误消息:ActiveRecord::RecordNotFound (Couldn't find all Tags with 'id': (3, 6, 1, 0) (found 3 results, but was looking for 4). Couldn't find Tag with id 0.):
刷新页面后,我可以看到标签已经存在,可以正常添加了。
这是我的控制器更新操作:
def update
respond_to do |format|
tagIds = []
params[:contact][:tag_ids].each do |tagName|
if tagName != '' && Tag.where(:name => tagName).blank?
Tag.create!(:name => tagName)
end
tagIds.push(Tag.where(:name => tagName).ids)
end
puts "contact_params is: #{contact_params}"
contact_params[:tag_ids] = tagIds
if @contact.update(contact_params)
format.html { redirect_to @contact, notice: 'Contact was successfully updated.' }
format.json { render :show, status: :ok, location: @contact }
else
format.html { render :edit }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
联系人参数 returns:{"name"=>"john smith", "image_url"=>"", "website"=>"", "bio"=>"", "phone"=>"", "email"=>"someone@gail.com", "location_info"=>"", "tag_ids"=>["", "3", "6", "1", "CBD"], "category_ids"=>[""]}
参数是: {“utf8”=>“✓”,“authenticity_token”=>“eIwSn9RGzMdds5jrewOvu1VT6ECadlRPCQa6CASQ7VhKgrqVtAYa7FKlVqk5t8IFQVv2b9yA5ruaa0NuT7DxwQ==”,“联系人”=>{“名称”=>“约翰·史密斯”,“[=>32=]” "", "email"=>"someone@gail.com", "image_url"=>"", "网站"=>"", "location_info"=>"", "bio"=>"", "tag_ids"=>["", "3", "6", "1", "CBD"], "category_ids"=>["" ]}, "提交"=>"更新联系人", "id"=>"1"}
Rails 方法 将使用 nested attributes 而不是将现有记录的 ID 与新标签混合在一起:
class Contact < ApplicationRecord
has_many :taggings
has_many :tags, though: :taggings
accepts_nested_attributes_for :tags
end
class Tag < ApplicationRecord
has_many :taggings
has_many :contacts, though: :taggings
validates_uniqueness_of :name
end
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :contact
end
您可以通过以下方式使用现有标签和新标签创建 contact/update:
Contact.create(
name: 'Bob',
tag_ids: [1, 2, 3],
tags_attributes: [
{ name: "foo" }, { name: "bar" }, { name: "baz" }
]
)
这不是唯一的方法,从用户体验的角度来看,这通常不是最佳解决方案,因为他们必须提交表单,然后发现标签已经存在,而且它也很容易创建一堆重复项/ 拼写错误。
一个好的替代方法是使用自动完成查询 TagsController#index
以在用户键入时按名称搜索标签以查找现有标签(如在 Whosebug 上看到的!)以及 ajax 调用如果标签不存在,则创建一个单独的 TagsController#create
方法。然后,通过向 select/checkbox 或隐藏输入添加选项,将返回的 id 添加到标签列表(tag_ids
参数)。
这会为用户提供即时有用的反馈,将关注点分离到后端并降低复杂性。
如果你想挽救你的方法,你可以通过:
class Contact < ApplicationRecord
# ...
accepts_nested_attributes_for :tags
end
class ContactsController < ApplicationController
def update
contact_params.merge!(
separate_tags(params[:contact][:tag_ids])
)
respond_to do |format|
if @contact.update(contact_params)
format.html { redirect_to @contact, notice: 'Contact was successfully updated.' }
format.json { render :show, status: :ok, location: @contact }
else
format.html { render :edit }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
private
def separate_tags(ids)
tags = Tag.where(tag_ids).ids.to_set
ids, names = tag_ids.separate do |id|
tags.include?(id)
end
{
tag_ids: ids,
tags_attributes: [
names.map {|name| { name: name }}
]
}
end
end
然而,使用混合了 ID 和名称以及 YMMV 的单个数组感觉像是一种非常 hacky 的方法。