Rails:如何使用has_and_belongs_to_many实现self join模式?

16

我想创建一个由多个类User组成的Users结构,每个用户可以有多个friends

class User < ActiveRecord::Base
  has_and_belongs_to_many :friends, class_name: "User"
end

我不需要了解他们之间的任何关系,因此我不使用:throughFriendship类一起。但现在我无法找到任何方法来创建相应的数据库(无论是使用迁移文件还是使用rails g model User username:string ...命令)。有什么想法吗?

我不需要详细了解他们之间的关系,因此我没有在Friendship类中使用:through。但现在我无法找到任何方法来创建相应的数据库(无论是使用迁移文件还是使用rails g model User username:string ...命令)。有什么想法吗?


我不太确定你所说的“我不需要他们之间的任何细节”是什么意思。你是指你只想要返回一个 User 对象数组吗? - Paul Richter
1
没错。我想直接连接它们,而不是通过另一个类。 - fakub
1个回答

39
以下是一些可能有用的资源: 我将总结在这些链接中找到的信息:
由于您描述了一个自我引用的多对多关系,因此您当然会得到一个连接表。通常情况下,连接表应该有一个明确的名称,以便Rails可以自动确定该表连接的模型,但是“自我引用”部分使得这有点棘手,但并不难。您只需要指定连接表的名称以及连接列即可。
您需要使用迁移创建此表,这个迁移可能看起来像这样:
class CreateFriendships < ActiveRecord::Migration
  def self.up
    create_table :friendships, id: false do |t|
      t.integer :user_id
      t.integer :friend_user_id
    end

    add_index(:friendships, [:user_id, :friend_user_id], :unique => true)
    add_index(:friendships, [:friend_user_id, :user_id], :unique => true)
  end

  def self.down
      remove_index(:friendships, [:friend_user_id, :user_id])
      remove_index(:friendships, [:user_id, :friend_user_id])
      drop_table :friendships
  end
end

我不确定是否有快捷的方法来创建这个表,但最简单的方法是使用rails g migration create_friendships命令,并填写self.upself.down方法。

然后在你的用户模型中,只需添加连接表的名称,如下所示:

class User < ActiveRecord::Base
  has_and_belongs_to_many :friends, 
              class_name: "User", 
              join_table: :friendships, 
              foreign_key: :user_id, 
              association_foreign_key: :friend_user_id
end

你可以看到,虽然数据库中有一个连接表,但没有相关的连接模型。
请告诉我这是否适用于你。

8
为什么要添加两个索引,一个是:user_id,另一个是:friend_user_id,并且排列顺序不同?为什么不只添加一个索引呢? - Diego Milán
在Github上,有人使用HABTM创建了一个自我引用关系的要点,但现在返回404错误。 - dmmfll
这两个索引是为了保证每个方向上的唯一性,Bob和Alice之间有友谊,但是可能Alice并没有和Bob交朋友,但是Bob不能和Alice有多个友谊,同样地,Alice也不能和Bob有多个友谊。 - Joe
1
@Joe 但是由于它们是不同的索引,它们真的解决了这样一个问题吗?即可能存在一个友谊(user_id=<bob>, friend_id=<alice>)和另一个友谊(user_id=<alice>, friend_id=<alice>)。从语义上讲,这应该是不可能的(它们是相同的友谊),但是这两个索引允许这种情况发生。 - Agis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接