I'm looking to make sure my methods are correct before pursuing this association. The implementation sounds too complicated, so I think there must be something wrong with my plan. I am using structured (SQL) data storage, conforming to the rails storage conventions. What I have is a User model, it has an email address, password_digest
, and name in the Schema.
class User < ActiveRecord::Base
has_many :posts
end
I'd like to implement a has_many
association to a friends collection, so that Users can belong_to
Users (as friends). I'm hoping to be able to have User.last.friends.last
return a User object when properly built and populated.
I believe I can create a model for this association like:
Class Friend < ActiveRecord::Base
belongs_to :user
belongs_to :friendlies, class: 'User'
end
Class User < ActiveRecord::Base
has_many :posts
has_many :friends
has_many :friendly, class: 'Friend'
end
But I think that will require me to add an as to the models and query using User.last.friends.last.user
So what I was thinking is this is a has_and_belongs_to_many
relationship. Can I get away with the following (or something similar):
class User < ActiveRecord::Base
has_and_belongs_to_many :friends, class: 'User'
end
I found this:
class User < ActiveRecord::Base
has_many :user_friendships
has_many :friends, through: :user_friendships
class UserFriendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
And this(self proclaimed 'standard'):
has_many :friendships, :dependent => :destroy
has_many :friends, :through => :friendships, :dependent => :destroy
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id", :dependent => :destroy
has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :dependent => :destroy
Which I assume requires a Friendship
model. I don't feel like I need a friendship model. I think that the class UserFriendship
method looks right, but it requires an additional model. Now onto the questions:
Can I do a has_and_belongs_to_many
relationship with a table that relates user to friends which are users, without incurring an additional model to do so?
Is it prudent to have an additional model 'in case' additional requirements pop up later?
What you're looking for is something called a self referential join
, which means that you can reference the same model
with a different association:
#app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :friendships,
class_name: "User",
join_table: :friendships,
foreign_key: :user_id,
association_foreign_key: :friend_user_id
end
You'd have to create a habtm
join table to have a reference:
$ rails g migration CreateFriendshipsTable
#db/migrate/create_friendships_table____.rb
class CreateFriendShipsTable < ActiveRecord::Migration
def change
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
end
$ rake db:migrate
There is a great reference here: Rails: self join scheme with has_and_belongs_to_many?
Is it prudent to have an additional model 'in case' additional requirements pop up later?
Only if you know you're going to have extra attributes. If not, you can get away with the habtm
for as long as you want.
You can try this
--friendship.rb
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend, :class_name => 'User'
end
--user.rb
class User < ApplicationRecord
has_many :friendships
has_many :friends, through: :friendships
end
--db migrate xxxx_create_friendship.rb
class CreateFriendships < ActiveRecord::Migration[5.0]
def change
create_table :friendships do |t|
t.belongs_to :user
t.belongs_to :friend, class: 'User'
t.timestamps
end
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With