Rails:has_many:仅在一侧通过关联

时间:2013-12-24 20:32:39

标签: ruby-on-rails ruby acts-as-taggable-on

我是ruby和ruby-on-rails的新手。我需要实现复杂的标记引擎:

  • 每个标签可以包含0个或更多个同义词(是的,就像在SO上一样)

  • 应该有一个层次结构标签:即有子标签和超级标签。说,我有三个标签:programmingrubyc。然后,programmingrubyc的超级标记,当我为项目输入关于ruby的标记时,没有必要输入标记programming,我可以仅使用ruby标记。当我输入标签时,应该递归添加所有超级标签。 (实际上很奇怪,我从来没有见过任何带有此功能的标签引擎,我真的感觉不够用它)

  • 每个用户都应拥有自己的一组标记,即每个Tag belongs_to User。标签不对任何人公开,它们对其所有者是私有的。

似乎我不能使用acts_as_taggable_on宝石,对吗?所以我必须实现自己的。实际上我已经实现了Tag类,它支持层次结构,同义词和用户的东西,它似乎有效。但是如何制作可以用它标记的东西还有很多问题。

我正在尝试对acts_as_taggable_on进行逆向工程,这对像我这样的新手来说真的很复杂..

所以,有很多问题,但我现在会更具体:

假设我有一个类Item,我希望它可以被标记。据我了解,我应该在has_many :throughItem方使用Tag关联。因此,创建包含item_idtag_id字段的中间表。

但是:我希望我的标记引擎具有通用性!因此,我不想将与Tag相关的任何内容添加到Item模型。

所以,唯一真正的问题是:通常只能在has_many :through方创建Item关联吗?

其次,如果有人对我解释过的东西有一些建议,我很乐意看到它们。

2 个答案:

答案 0 :(得分:2)

我可以向你推荐一些可能对你的大部分问题陈述有帮助的事情。

应该有标签层次结构

可以使用自引用has_many关联 Creating a model that has a tree structure

class Tag < ActiveRecord::Base
  has_many :sub_tags, class_name: "Tag", :foreign_key => "super_tag_id"
  belongs_to :super_tag, class_name: "Tag"
end

我希望我的标记引擎具有通用性!所以我不想添加任何与Item

相关的Tag模型

标记应该是多态的,因为它广泛适用于使用。

class TaggedItem < ActiveRecord::Base
  belongs_to :tag
  belongs_to :taggable, polymorphic: true
end

创建has_many通常是否可以:仅通过物品方面的关联?

我想是的,has_may through是要走的路。因此,您的Item模型可能看起来像

class Item < ActiveRecord::Base
  has_many :tags, as: :taggable, through: :tagged_items
end

答案 1 :(得分:1)

除了swapnilabnave的好答案外,我还会解释你在寻找什么:

我建议将您的问题分解为更多模块化问题

您的问题是,您是否可以使用has_many :through表示关系。正如所解释的那样,答案是“是”,但这并不能完全解决问题


标记层次结构

如上所述,您的代码将包含“父母”和其他商品

虽然我在Rails中没有丰富的经验,但在CakePHP中,这将被称为tree structure。 CakePHP通过3列 - parent_idleftright来处理这个问题。这些基本上允许应用程序根据这3列

中使用的数字专门显示树中的各种项目

在您的情况下,我建议在parent_id数据库中添加tags列,如果需要,您可以为其分配超级标记(似乎您只有一个超级-tag每个标签?)。您可以使用swapnilabnave发布的self-referencing association技术在您的模型中处理此问题,以便能够像这样调用super_tag

@tag.super_tag 

重要的是,如果每个标签只能有一个super_tag,那么这只会有效。如果你想为每个标签提供无限数量的超级标签,你可以这样做:

class Tag < ActiveRecord::Base
  has_many :sub_tags, class_name: "Tag", :foreign_key => "super_tag_id"

  has_many :tag_super_tags, class_name: "TagSuperTag", :foreign_key => "super_tag_id"
  has_many :super_tags, :through => :tag_super_tags
end

Class TagSuperTag
    belongs_to :tag
    belongs_to :super_tag, :class => "Tag"
end

虽然我不确定连接模型代码是否有效,但希望能在这里向您展示这个想法。这将创建一个名为TagSuperTag的自引用连接模型,并允许您拥有这样的表:

tag_super_tags
id | tag_id | super_tag_id | created_at | updated_at

tags (no need for super_tag_id)
id | user_id | name | etc 

这将允许您为每个标签添加任意数量的超级标签


用户有很多标签

因为每个用户都有很多标签,所以您可以使用标准has_many关联,如下所示:

class User < ActiveRecord::Base
  has_many :tags
end

class Tag < ActiveRecord::Base
 belongs_to :user
end

这意味着每个代码都必须有user_id列,才能充当foreign_key


标签是通用的

使标签具有通用性(不只是item)需要polymorphic association,而且似乎是一个连接模型

正如swapnilabnave所描述的那样,任何想用它来存储标签的模型都可以访问这个连接模型,从而允许你将任何模型与它相关联。这是你如何做到这一点:

class TaggedItem < ActiveRecord::Base
    belongs_to :taggable, polymorphic: true
    belongs_to :tag
end

tagged_items
id | taggable_type | taggable_id | tag_id | created_at | updated_at

这将允许您从任何其他模型引用此模型,如下所示:

class Post < ActiveRecord::Base
    has_many :tagged_items, :class_name => "TaggedItem", :as => :taggable, :dependent => :destroy
    has_many :tags, :through => :tagged_items
end

class Email < ActiveRecord::Base
    has_many :tagged_items, :class_name => "TaggedItem", :as => :taggable, :dependent => :destroy
    has_many :tags, :through => :tagged_items
end

Tag模型也应该has_many :tagged_items

class Tag < ActiveRecord::Base
    has_many :tagged_items, :class_name => "TaggedItem", :foreign_key => "tag_id", :dependent => :destroy
end

希望这有帮助