在 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 的方法。