Self-referencing relationships

I have a question on how to approach a simple self-referencing data model in Rails:

I have a table called "relationships":

create_table :relationships do |t| t.integer :user_id t.integer :is_relationship_with t.string :relationship

t.timestamps end

And a table called "user"

create_table "users", :force => true do |t|   t.column :login, :string   t.column :email, :string   #... and so on... end

A relationship could be one of the following: friend_of, student_of, parent_of. Any user should be able to have one or more of these, but of course should be defined only once as one of these three options.

My question is, how would I do this in the model? I am assuming it would go into my model/relationships.rb file using something like a has_and_belongs_to_many instruction, but not sure how that should look given the recursive nature of this data model. Any strategic advice would be most appreciated!

Cheers, Joe

Just a correction: Any user could have any number of such relationships. (Started this question out using my Roles model where that would have made more sense...)

And to further clarify my Relationships data:

t.integer :user_id # is the obvious foreign key back to the user table. t.integer :is_relationship # is the foreign key back to the user table for whom this relationship is being applied. (name changed slightly from earlier post) t.string :relationship # can be friend_of, student_of, parent_of. replaces the _relationship_ part of the above field.

I can see in my relationships.rb model that this might be a start:

class Relationship < ActiveRecord::Base   has_many :users, :through => :relationships end

But I'm confused about what to do about the :is_relationship part, and in turn what I would need to put in the user model - has_many :relationships ?

I was just looking for a solution to this myself! I didn't find one so I had a play, and came up with the following example. It all seemed to work quite nicely so I thought I'd post it


I’ve written a tutorial on this now because it’s such a frequently asked question: