当记录未被持久化时,使用标签创建新记录(has_many_through)

时间:2017-01-07 21:45:17

标签: ruby-on-rails has-many-through

我想实现自定义标记功能。到目前为止,我有ItemTag模型,但我很难提交标记 - 每当我尝试创建新的{{1}时,Rails就会告诉我标记分配无效 }。

(新项目的创建表示项目描述的文本区域以及来自db的所有现有标签的一系列复选框。)

奇怪地更新现有项目的标签工作正常。这一观察让我发现,罪魁祸首在于,在构建期间,item对象不会被持久化,因此它还没有Item,因此此时无法建立项目和标记之间的关系。

基于此,我在id中使用以下装置增强了create操作。 虽然这样做对我来说似乎是丑陋的黑客,所以我想知道正确的方法™来处理这种情况。

ItemsController

<小时/>

相关代码

class ItemsController < ApplicationController
⋮
  def create
    tags = {}
    tags[:tag_ids] = item_params.delete('tag_ids')
    reduced_params = item_params.reject{|k,v| k == 'tag_ids'}
    @item = current_user.items.build(reduced_params)
    @item.save
    @item.update_attributes(tags)
class Item < ApplicationRecord
  has_many :tag_assignments, foreign_key: 'tagged_item_id'
  has_many :tags, through: :tag_assignments
  ⋮
class TagAssignment < ApplicationRecord
  belongs_to :tagged_item, class_name: 'Item'
  belongs_to :tag
  ⋮

items_controller.rb

class Tag < ApplicationRecord
  has_many :tag_assignments
  has_many :items, through: :tag_assignments
  ⋮

_item_form.html.erb

def create
    @item = current_user.items.build(item_params)
    ⋮
private

  def item_params
    params.require(:item).permit(:description, :tag_ids => [])
  end

2 个答案:

答案 0 :(得分:1)

您可能希望使用虚拟属性,在本例中为 tag_list ,它将使用数据库中存在的新标记或已创建标记和持久标记。

class Item < ApplicationRecord
  def tag_list
    self.tags.collect do |tag|
    tag.name
    end.join(", ")
   end

   def tag_list=(tags_string)
    tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
    new_or_found_tags = tag_names.collect { |name| Tag.find_or_create_by(name: name) }
    self.tags = new_or_found_tags
   end

现在,在您的商品表单中,您只需要引用虚拟属性而不是

 <%= f.label :tag_list %><br />
 <%= f.text_field :tag_list %>

这应该很简单,并且应该导致整体代码更简单。您也不要忘记相应地更改当前代码。例如,在您的项目控制器

private

  def item_params
    params.require(:item).permit(:description, :tag_list)
  end

现在您的项目new和创建操作可以像

一样简单
def new
    @item = Item.new
end

def create
    @item = Item.new(item_params)
    @item.save
    redirect_to ......
end

答案 1 :(得分:0)

我建议使用acts_as_taggable_on gem。它解决了几乎所有关于标签使用的情况。

class User < ActiveRecord::Base
  acts_as_taggable # Alias for acts_as_taggable_on :tags
  acts_as_taggable_on :skills, :interests
end

class UsersController < ApplicationController
  def user_params
    params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
  end
end

@user = User.new(:name => "Bobby")

@user.tag_list.add("awesome")   # add a single tag. alias for <<
@user.tag_list.remove("awesome") # remove a single tag