15
votes

I would like to create a structure of Users having many friends, also of class User:

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

I do not need any details of their relationship thus I do not use :through with kind of class Friendship. But now I cannot find any way how to create corresponding database (neither with migration file nor using rails g model User username:string ... command). Any ideas?

1
I'm not quite sure what you mean by "I do not need any details of their relationship". Do you mean you simply want to get back an array of User objects?Paul Richter
Exactly. I would like to connect them directly, not through another class.fakub

1 Answers

39
votes

Here are some resources which may be helpful:

I'll summarize the information found in those links:

Given that you're describing a self-referential many-to-many relationship, you will of course end up with a join table. Normally, the join table should be deliberately named in such a way that Rails will automatically figure out which models the table is joining, however the "self-referential" part makes this a tad awkward, but not difficult. You'll merely have to specify the name of the join table, as well as the joining columns.

You'll need to create this table using a migration that will probably look something like this:

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

I'm not certain whether there is a shortcut way of creating this table, but bare minimum you can simply do rails g migration create_friendships, and fill in the self.up and self.down methods.

And then finally in your user model, you simply add the name of the join table, like so:

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

As you can see, while you do have a join table in the database, there is no related join model.

Please let me know whether this works for you.