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

时间:2022-09-23 08:00:04

I am new to ruby and ruby-on-rails. I need to implement complicated tagging engine:

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

  • each tag could have 0 or more synonyms (yep, just like on SO)

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

  • there should be an hierarchy of tags: i.e. there are sub-tags and super-tags. Say, I have three tags: programming, ruby and c. Then, programming is a super-tag of both ruby and c, and when I enter tags for item about ruby, it's not necessary to enter tag programming, I can only tag it with ruby. When I enter tags, all the super-tags should be added recursively. (actually it's strange I've never seen any tags engine with this feature, I really feel lack of it)

    应该有一个标签层次结构:即有子标签和超级标签。说,我有三个标签:编程,ruby和c。然后,编程是ruby和c的超级标签,当我输入关于ruby的项目的标签时,没有必要输入标签编程,我只能用ruby标记它。当我输入标签时,应该递归添加所有超级标签。 (实际上很奇怪我从来没有见过有这个功能的任何标签引擎,我真的觉得缺乏它)

  • each user should have its own set of tags, i.e. each Tag belongs_to User. Tags aren't public to anyone, they are private to its owner.

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

It seems I can't use acts_as_taggable_on gem, right? So I have to implement my own one. Actually I have already implemented Tag class that supports hierarchy, synonyms and users stuff, it seems to work. But there are a lot of questions how to make something taggable with it.

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

I am trying to reverse-engineer acts_as_taggable_on, it is really complicated for a newbie like me..

我正在尝试逆向工程act_as_taggable_on,这对我这样的新手来说真的很复杂。

So, there are a lot of questions, but I'm going to be more specific now:

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

Assume I have a class Item that I want to be taggable. As far as I understand, I should use has_many :through association, both on Item and Tag side. And therefore create intermediate table with item_id and tag_id fields.

假设我有一个我想要标记的类Item。据我所知,我应该使用has_many:通过关联,在Item和Tag方面。因此,使用item_id和tag_id字段创建中间表。

BUT: I want my tagging engine to be universal! So I don't want to add to Tag model anything related to Item.

但是:我希望我的标记引擎是通用的!所以我不想添加任何与Item相关的Tag模型。

So, the only real question is: is it generally ok to create has_many :through association only on Item side?

所以,唯一真正的问题是:创建has_many通常是否可以:只在Item方面通过关联?

And secondly, if anyone has some suggestions on the stuff I explained, I'd be happy to see them.

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

2 个解决方案

#1


1  

Further to swapnilabnave's great answer, I'll explain what you're looking for:

继续使用swapnilabnave的答案,我会解释你在寻找什么:

I would recommend breaking up your problem into more modular issues

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

Your question is whether you can use has_many :through for the relation. The answer, as explained, is "yes", but that won't solve the problem in its entirety

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


Hierarchy Of Tags

标签层次结构

As mentioned, your tags will have "parents" and other items

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

Although I don't have huge experience with this in Rails, in CakePHP, this will be known as a tree structure. CakePHP handles this by having 3 columns - parent_id, left, right. These basically allow the app to specifically show the various items in your tree based on which numbers are used in those 3 columns

虽然我在Rails中没有很多经验,但在CakePHP中,这将被称为树结构。 CakePHP通过3列 - parent_id,left,right来处理这个问题。这些基本上允许应用程序根据这3列中使用的数字专门显示树中的各种项目

In your case, I'd recommend putting a parent_id column in the tags database, which you'll be able to assign a super tag to if required (it seems you only have one super-tag per tag?). You could handle this in your model using the self-referencing association technique that swapnilabnave posted to give the ability to call the super_tag like this:

在您的情况下,我建议在标签数据库中放置一个parent_id列,如果需要,您可以为其分配一个超级标签(似乎每个标签只有一个超级标签?)。您可以使用swapnilabnave发布的自引用关联技术在您的模型中处理此问题,以便能够像这样调用super_tag:

@tag.super_tag 

And important note would be that this would only work if you could only have one super_tag per tag. If you wanted an unlimited number of super tags per tag, you'd do this:

重要的是,如果每个标签只能有一个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

Although I'm not sure if the join model code will work, it will hopefully show you the idea here. This would create a self-referencing join model called TagSuperTag, and allow you to have a table like this:

虽然我不确定连接模型代码是否有效,但它有望在这里向您展示这个想法。这将创建一个名为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 

This will allow you to add as many super tags as you wish to each tag

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


User Has Many Tags

用户有很多标签

Because each user will have many tags, you can just use the standard has_many association, like this:

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

class User < ActiveRecord::Base
  has_many :tags
end

class Tag < ActiveRecord::Base
 belongs_to :user
end

This means that each tag will have to have a user_id column, to act as a foreign_key

这意味着每个标记必须具有user_id列,以充当foreign_key


Tags Are Universal

标签是通用的

Making tags universal (not just for item) will require a polymorphic association, and what seems to be a join model

使标签通用(不仅仅是项目)将需要多态关联,以及似乎是连接模型

As swapnilabnave has described, this join model can be accessed by any model which wants to use it to store tags, thus allowing you to associate any model with it. Here's how you could do this:

正如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

This will allow you to reference this model from any other, like this:

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

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

And the Tag model should has_many :tagged_items, too:

Tag模型也应该是has_many:tagged_items:

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

Hope this helps

希望这可以帮助

#2


2  

I can suggest you few things that might help some/most of your problem statements.

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

there should be an hierarchy of tags

应该有一个标签层次结构

self referential has_many association can be used Creating a model that has a tree structure

可以使用self referential has_many association创建具有树结构的模型

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

I want my tagging engine to be universal! So I don't want to add to Tag model anything related to Item

我希望我的标记引擎是通用的!所以我不想添加任何与Item相关的Tag模型

Tagging should be polymorphic, as its widely applicable as used.

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

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

is it generally ok to create has_many :through association only on Item side?

创建has_many通常是否可以:只在Item方面通过关联?

I think yes, has_may through is the way to go. So your Item model may look like

我想是的,has_may通过是要走的路。所以你的Item模型可能看起来像

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

#1


1  

Further to swapnilabnave's great answer, I'll explain what you're looking for:

继续使用swapnilabnave的答案,我会解释你在寻找什么:

I would recommend breaking up your problem into more modular issues

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

Your question is whether you can use has_many :through for the relation. The answer, as explained, is "yes", but that won't solve the problem in its entirety

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


Hierarchy Of Tags

标签层次结构

As mentioned, your tags will have "parents" and other items

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

Although I don't have huge experience with this in Rails, in CakePHP, this will be known as a tree structure. CakePHP handles this by having 3 columns - parent_id, left, right. These basically allow the app to specifically show the various items in your tree based on which numbers are used in those 3 columns

虽然我在Rails中没有很多经验,但在CakePHP中,这将被称为树结构。 CakePHP通过3列 - parent_id,left,right来处理这个问题。这些基本上允许应用程序根据这3列中使用的数字专门显示树中的各种项目

In your case, I'd recommend putting a parent_id column in the tags database, which you'll be able to assign a super tag to if required (it seems you only have one super-tag per tag?). You could handle this in your model using the self-referencing association technique that swapnilabnave posted to give the ability to call the super_tag like this:

在您的情况下,我建议在标签数据库中放置一个parent_id列,如果需要,您可以为其分配一个超级标签(似乎每个标签只有一个超级标签?)。您可以使用swapnilabnave发布的自引用关联技术在您的模型中处理此问题,以便能够像这样调用super_tag:

@tag.super_tag 

And important note would be that this would only work if you could only have one super_tag per tag. If you wanted an unlimited number of super tags per tag, you'd do this:

重要的是,如果每个标签只能有一个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

Although I'm not sure if the join model code will work, it will hopefully show you the idea here. This would create a self-referencing join model called TagSuperTag, and allow you to have a table like this:

虽然我不确定连接模型代码是否有效,但它有望在这里向您展示这个想法。这将创建一个名为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 

This will allow you to add as many super tags as you wish to each tag

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


User Has Many Tags

用户有很多标签

Because each user will have many tags, you can just use the standard has_many association, like this:

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

class User < ActiveRecord::Base
  has_many :tags
end

class Tag < ActiveRecord::Base
 belongs_to :user
end

This means that each tag will have to have a user_id column, to act as a foreign_key

这意味着每个标记必须具有user_id列,以充当foreign_key


Tags Are Universal

标签是通用的

Making tags universal (not just for item) will require a polymorphic association, and what seems to be a join model

使标签通用(不仅仅是项目)将需要多态关联,以及似乎是连接模型

As swapnilabnave has described, this join model can be accessed by any model which wants to use it to store tags, thus allowing you to associate any model with it. Here's how you could do this:

正如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

This will allow you to reference this model from any other, like this:

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

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

And the Tag model should has_many :tagged_items, too:

Tag模型也应该是has_many:tagged_items:

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

Hope this helps

希望这可以帮助

#2


2  

I can suggest you few things that might help some/most of your problem statements.

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

there should be an hierarchy of tags

应该有一个标签层次结构

self referential has_many association can be used Creating a model that has a tree structure

可以使用self referential has_many association创建具有树结构的模型

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

I want my tagging engine to be universal! So I don't want to add to Tag model anything related to Item

我希望我的标记引擎是通用的!所以我不想添加任何与Item相关的Tag模型

Tagging should be polymorphic, as its widely applicable as used.

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

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

is it generally ok to create has_many :through association only on Item side?

创建has_many通常是否可以:只在Item方面通过关联?

I think yes, has_may through is the way to go. So your Item model may look like

我想是的,has_may通过是要走的路。所以你的Item模型可能看起来像

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