self-reference

This blog covers associations within a model useful to modelling relationships such as friends on facebook.

I am working on a similar scenario where I had to design a model where a user is friends with other users(many to many relationship).

The real challenge here is that all the users are going to be stored in the User model. That way we somehow have to decide how to generate the backward relationship i.e. accessing friends of a particular user.

Designing this relationship requires one more model which will be used only to store the relationship of user and its friends.

For accessing friends of a particular user we need to have a foriegn_key(user_id) which will be used to fetch all the users(friends) which are related to a particular user.

Before we tackle what we actually want to do, let’s first dive in model relationships and try to understand how associations work inside of models in rails.

Consider a User model where a user belongs to a company.

class User < ActiveRecord::Base
    belongs_to :company
end

If I have describe relationship in above code in one line this is how I would.

Rails internally adds foriegn_key attribute in user table with name campany_id.

Under the Hood.

  1. Rails derives the class name Company using :company and some little ruby magic.
  2. By default the foreign_key is added with the name of Model, in this case it would be company_id.

Interestingly we can override above behaviour.

 

class User < ActiveRecord::Base
    belongs_to :company, :class_name => 'User', :foreign_key => 'user_id'
end

We can specify the class_name & foreign_key here we are doing nothing but overriding rails process where it decides which class to call and which attribute to use as foreign_key.

This way user model will be used rather than company model for this association and foreign_key attribute would be user_id instead of company_id.

Back to our original friends in a social network

Above I mentioned using a second table which will store the relationship between a user and its friends.

This second table should have two columns user_id and friend_id(i’ll come to friend_id after this.)

So, for a particular user with id = 1 having 2 friend connections we will have 2 records in this new table with attrs.

  • NewModel id: 1, friend_id: 2, user_id: 1
  • NewModel id: 2, friend_id: 3, user_id: 1

We have another model to keep track of friends of a user lets think of a name(a good one)…:) Since this model is maintaining friendship b/w users Friendship sounds like a good choice.

Let’s go back to our User model and see how to relate it to Friendship model.

A user can have many friends. It implies that for a given user friendship model will have many records. So a user has many friendships. We also want to be able to access friends of a user for that we should add a has_many :friends Here we are using has_many: through relationship.

class User < ActiveRecord::Base
    has_many :friendships
    has_many :friends, :through => :friendships
end

Some Key points to note in User model

  1. has_many :friendships is the association which actually tells rails that we are going to have multiple records of Friendship model for one record of Usermodel.
  2. has_many :friends, :through => :friendships this line actually tells rails that we will be using .friends to access all the records that are related to object calling friends.
  3. Key thing to note here is through relationship tells rails that friendship table will be used to find out friends.

Lets now define Friendship model.

class Friendship < ActiveRecord:Base
    belongs_to :user                            <!--# This ads the user_id column as foreign key-->
    belongs_to :friend, :class_name => "User"   <!--# This ads as friend_id as the foriegn key but essentially points to objects of User class-->
end

Friendship model has two attributes user_id & friend_id these both are basically same model id’s.

  1. belongs_to :user implicitly ads foriegn_key as user_id remember rails uses :user and does some magic on it.
  2. belongs_to :friend it points to User class and ads foriegn_key as friend_idsince we did not override its foriengn_key.

After this .friends on any user object can be used to fetch all the friends of that user.

All set we now have a working model of friends on a social network. Voilla!!!

After this post I can say I have moved closer to understanding association in rails.

This blog post originally appeared on ashish singh’s blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s