Ruby on Rails Hidden Secrets: How To Get The Most Out Of Active Record Associations | Hacker Noon

Author profile picture

Hello folks! In this article, we are going to unravel the mystery behind the Rails Active Record class. To be honest, I struggled a lot with Rails models as a beginner. I spent a lot of time reading the docs, read a couple of medium articles, watched some youtube videos but all in vain. I have chosen to draft a nice article that constitutes of baby steps that is suitable for aspiring Rails Engineers.

I chose a Facebook database model since it is a little bit advanced as well as it encompasses the majority of the rails association concepts.


  1. User-post Association
  2. User Friendship Association


Active Record: the layer responsible for representing business data and logic

Object Relation Mapping(ORM): this is a technique used to connect objects of an application to tables in a Relationship Database management system. With this, we can store and retrieve data without writing a single SQL statement.


  1. Basic knowledge of Ruby programming language.
  2. The latest version of Ruby. Click here for more info on how to install Ruby.
  3. Node.
  4. Rails.
  5. SQlite / Postgresql
  6. Problem-solving attitude.

Getting Started

Enough with the chit chat, let’s get our hand dirty.

#1 Fire your terminal {ctr + Alt + T} on a Unix system and generate a new Rails app with the name

rails new facebook-db-clone && cd facebook-db-clone

2 -> Fire rails server to confirm that everything works 🙂

rails server

#3 -> Scaffold the User, Post, and Comments model for a start. if you are confused, here is an ERD {Entity Relationship Diagram} diagram that will help you visualize the overall structure of the models.

Let’s start by generating the user model.

Here we design the user model by explicitly naming the user attributes and passing them to the

 rails g model 

command. The User in this case is the model class. The command, in turn, generates several files by invoking the active_record method. The files consist mainly of

unit tests

, migration file ( a blueprint that helps developers to define changes to their database schema, making it possible to use a version control system to keep things synchronized with the actual code ) and a



Having cleared the air, it is time we replicate the same procedure for the other two models:- the




. Run the following commands in successions.

➜ rails g model Post body:text
➜ rails g model Comments content:text

After running the above commands, there should be three files with their names prefixed with a timestamp,


 in the


directory where the timestamp is the UTC formatted date and time that the migration was generated. The naming convention adopted by rails here proves to be useful when determining the order of operation later when we ran our migrations.

Here, I have included a snapshot of the three migration files.

Each and every class inherits from the


class. The 3 classes each holds a recipe of how Rails is creates the tables and rows of our User, Posts, and Comments in the database. Rails is actually good at abstracting the tedious work of writing SQL statements, Pretty sweet right. For example, to the left of the snapshot, Rails is creates a table named


and rows

 first_name, last_name, email and password 

and so forth. In some cases, migration files might contain other advanced methods i.e




methods, they describe the transformation required to implement or remove the migrations. click here for more info.

#4 Tattooing the Database

By default, Rails uses


out of the box, as it primary database coupled with zero configuration. Thanks to this, one can focus on grasping the problem at hand. For production purposes, you might need to consider other options i.e


since SQLite is barely supported on cloud services i.e Heroku. Our next move is to execute the instruction held by the classes. To achieve this, we ran the following commands.

➜ rails db:create
➜ rails db:migrate

rails db:create creates a database based on the sqlite config file whereas rails db:migrate creates the tables and rows used to store the user data.


spoiler Alert: Nitty-gritty staff

Association: – The connection between two or more Active Record models i.e user vs post model.

Rails support six associations:







has_many :through,
has_one :through




Relationships are implemented using macros-style calls. this allows you to declaratively add features to your models with ease.

One-to-many Relationship.

belongs_to: User vs post model Association:

class User < ApplicationRecord
  # write your association staff here

class Post < ApplicationRecord
  # write your association staff here
  • User can have zero{

    or multiple Posts instances.

  • A post instance can only belong to only one instance of a User and it cannot exist on it’s own. By this I mean, If we destroy a User, all the posts related to that user should also be destroyed.

From the look of things, this is a


relationship. To implement this type of relationship, Rails provides us with




methods. These methods instructs rails to maintain a Primary key – Foreign Key relationship between the instances of the two models.

#1 In your model’s folder, open




file. Add the following methods.

class User < ApplicationRecord
  # write your association staff here
  has_many: posts
class Post < ApplicationRecord
  # write your association staff here
  belongs_to: user

#2 Generate a new migration that adds a primary key – foreign key reference to both the User and Post model.

#3 Edit the newly created migration file


. Add the following content.

class AddUserIdToPosts < ActiveRecord::Migration[5.2]
  def change
    add_column :posts, :user_id, :string
    add_index :posts, :user_id

This migration is pretty straightforward. It adds a column


to the


table and also sets the


column as the

foreign key

pointing to the user instanceCyrus-Kiprop. Run

rails db:migrate

to persist the changes.

Rails console 

allows you to interact with your rails application from the command line. Open a new terminal session at the root of your app and run.

rails console



to apply recent changes.

Create a new user:

➜ new_user = User.create(first_name: "John", last_name: "Doe", email: "[email protected]", password: "password")


Create two new posts, authored by the above user.

➜ user = User.first
➜ new_post1 ="This is my first Article, please give it a star")
➜ new_post2 = "This is my second Article, please give it a star if you like")

Access all the posts authored by John Doe

➜ user = User.where("first_name = ?", "John")
➜ user.posts

Access the author of a Post:

p1 = Post.first

Congratulations!!! That is it for a one-to-many relationship. Be sure to visit Rails docs for a better understanding.

Many-to-many: Association

Friendships This section is a little tricky and rough. It is upon you to stick with me on this one. Let’s think for a second, how do we model this? which tables are involved? For sure, there are multiple ways to achieve this, but the main goal here is to understand the concept behind friendships.

  1. Users can send and receive friend requests from other users.
  2. Users can multiple friends.
  3. Users can accept friend requests as well as reject any sort of invitation.
  4. We need to delete a friendship/invitation record upon rejection/unfriend action from the user.
  5. Confirmed friendship is reciprocal to both parties.

The difficulty here lies in creating two join tables(the general norm in many-to-many relationships) that reference both the users and the friend(user). This is because the


table is going to be referencing itself. Wait, what! self referencing, yeah you read it right.

Our Plan:

  • Create a friendship table to keep track of the requests/invitations.
  • Keep track of the status of the invitation by adding an is_comfirmed column to the friendship table.


#1 Scaffold the Friendship model

rails g model Friendship user:references friend:references confirmed:bolean

We all know the output of the above rails command. Yeah, what about the


keyword? The keyword is a fancy way of hooking up different models by adding foreign key columns to the newly created table that act as pointer.

The odd part of the command is that it is trying to reference a non-existing friends table. To fix this, open your migration files under db/migration directory and change it to resemble this.

class CreateFriendships < ActiveRecord::Migration[5.2]
  def change
    create_table :friendships do |t|
      t.references :user, foreign_key: true
      t.references :friend, index: true
      t.boolean :confirmed

    add_foreing_key :friendships, :users, column: :friend_id

using the


method above allows us to reference the user table as a friends table. Go ahead and perform the data migrations.

rails db:migrate

#2 Update the Relationships on the Friendship model

Edit the Friendship class to be similar to this.

class Friendship < ApplicationRecord
  belongs_to :user
  belongs_to :friend, :class_name => 'User'
Belongs_to :friend,  :class_name => 'User' 

points the


table to the


table since a friend is also a user.

#3 Update the Relationship in the User Model

class User < ApplicationRecord
  has_many :posts
  has_many :friendships
  has_many :reciprocal_friends, :class_name => "friendships", :foreign_key => "friend_id"

#4 Built custom Helper methods to:

Open your User model and add the following methods;

Method 1: Get all friends of a specific user.

 def friends
    direct_friends = { |friendship| friendship.friend if friendship.confirmed }
    inverse_friends = { |friendship| friendship.user if friendship.confirmed }
    (direct_friends + inverse_friends).compact

Method 2: Keep track of pending invitations.

# pending invitations
  def pending_friend_requests { |friendship| friendship.friend unless friendship.confirmed }.compact

Method 3: Incoming friend Requests.

 # incoming friend requests
  def incoming_friend_requests { |friendship| friendship.user unless friendship.confirmed }.compact

Method 4: Confirm Friend Requests

def confirm_friend_request?(user)
    friend = reciprocal_friends.find { |friendship| friendship.user == user }
    if friend
      friend.confirmed = true

Method 5: Check whether a user is already a friend.

def is_friend?(user)

Well, that was quite a lot to take in. The above code is pretty straightforward for a Rubyist. This is a bare minimum backend configuration that you can use as a template of your next social site. Stay tuned for the next couple of Episodes on




part of the associations.


The Noonification banner

Subscribe to get your daily round-up of top tech stories!

read original article here