0

我正在尝试对带有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

ActiveRecord::HasOneThroughCantAssociateThroughCollection当我尝试执行 Item.find(1).type 时,这给了我一个提示。

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

编辑:我还希望能够通过执行 item.type 和 item.use 等找到三个预定义的标签。

4

2 回答 2

1

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

表:标签

  • ID
  • 标签名

表:ItemTag

  • ID
  • item_id
  • tag_id

表:项目

  • ID
  • type_id
  • material_id
  • 使用_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

因此,您的数据库将在 item 表中直接包含三列,它们链接到 tag 表。这些是通过验证所必需的,但如果您愿意,您也可以在迁移中设置这些列不为空。其他可选标签保持相同的关系。

您需要belongs_to 而不是has_one,因为这会将关系推向您想要的Item。Has_one 在 Tag 表中放置了一个 item_id 列,这不是您想要的。

要通过此方法使三个必需的标签与其余标签一起出现,我建议添加一个仅用于此用途的函数,定义为上面的 list_tags。

希望有帮助!

于 2013-07-09T19:56:43.013 回答
0

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

商品型号:

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(...)根据您确定类型/材料/用途的构成方式来调整查询。

于 2013-07-09T19:57:08.067 回答