Rails:如何为具有多个标签的项目建模,其中需要一些标签?

时间:2013-07-09 19:19:09

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

我正在尝试为has_many标记的项目建模。项目可以有多个标签,但要求至少有3个预定义标签。

这是我到目前为止所拥有的:

class Item < ActiveRecord::Base
  has_one :type, :through => :item_tags, :source => :tag
  has_one :material, :through => :item_tags, :source => :tag
  has_one :use, :through => :item_tags, :source => :tag
  has_many :tag, :through => :item_tags
  has_many :item_tags
end

当我尝试使用Item.find(1).type时,这给了我ActiveRecord::HasOneThroughCantAssociateThroughCollection

我不知道该怎么做。有人可以帮忙吗?

编辑:我也想通过item.type和item.use等找到三个预定义的标签。

2 个答案:

答案 0 :(得分:1)

通过了解您希望如何设置数据库,首先考虑这一点会更容易。你想要:

表:标签

  • ID
  • TAG_NAME

表:ItemTag

  • ID
  • ITEM_ID
  • TAG_ID

表:项目

  • ID
  • TYPE_ID
  • material_id
  • use_id

所以,你的模型更像是:

class Item < ActiveRecord::Base
  belongs_to :type, :class_name => 'Tag'
  belongs_to :material, :class_name => 'Tag'
  belongs_to :use, :class_name => 'Tag'

  # Require these tags
  validates_presence_of :type, :material, :use

  has_many :item_tags
  has_many :tags, :through => :item_tags

  def list_tags
    [type, material, use] + tags
  end
end

因此,您的数据库将直接在项目表中包含三列,这些列链接到标记表。这些是通过验证所必需的,但您也可以在迁移中进行设置,以便在您希望的情况下使这些列不为空。其他可选标签保持相同的关系。

您想要belongs_to而不是has_one,因为这会将关系推送到您想要的项目。 Has_one将一个item_id列放在Tag表中,这不是您想要的。

要通过此方法使用其他标记显示三个必需的标记,我建议添加一个仅供此用途的函数,定义为上面的list_tags。

希望有所帮助!

答案 1 :(得分:0)

我认为您可能希望使用自定义验证来检查Item.tags是否包含您需要的那些,然后使用范围和类方法来使item.use,item.type等按您的意愿工作。< / p>

物品型号:

class Item < ActiveRecord::Base
  has_many :tags, :class_name => 'ItemTag'

  validate :has_type, :has_use, :has_material

  # Access methods
  def types
    self.tags.types
  end
  def uses
    self.tags.uses
  end
  def materials
    self.tags.materials
  end

  private

  # Custom validation methods
  def has_type
    unless tags.present? and tags.include?(ItemTag.types)
      errors.add("Tags must include a type.")
    end
  end

  def has_material
    unless tags.present? and tags.include?(ItemTag.materials)
      errors.add("Tags must include a material.")
    end
  end

  def has_use
    unless tags.present? and tags.include?(ItemTag.use)
      errors.add("Tags must include a use.")
    end
  end
end

ItemTag模型:

class ItemTag < ActiveRecord::Base
  scope :types, lambda { where(...) }
  scope :materials, lambda { where(...) }
  scope :uses, lambda { where(...) }
end

如果首选使用访问方法中的.first,您可以抓住单次出现。您需要根据确定类型/材料/用途的方式来调整where(...)查询。