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. Deliverables User-post Association User Friendship Association Terminologies: the layer responsible for representing business data and logic Active Record: 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. Object Relation Mapping(ORM): Pre-requisites Basic knowledge of Ruby programming language. The latest version of Ruby. Click for more info on how to install Ruby. here Node. Rails. SQlite / Postgresql 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 facebook-Db-clone 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 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 , 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 file. rails g model unit tests user.rb Having cleared the air, it is time we replicate the same procedure for the other two models:- the and . Run the following commands in successions. post comments ➜ 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. stimestamp_my_new_migration.rb db/migrate/ 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 and so forth. In some cases, migration files might contain other advanced methods i.e and methods, they describe the transformation required to implement or remove the migrations. click for more info. ActiveRecord::Migration users first_name, last_name, email and password up down here #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. SQlite Postgresql ➜ rails create ➜ rails migrate db: db: 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. Associations: spoiler Alert: Nitty-gritty staff The connection between two or more Active Record models i.e user vs post model. Association: - Rails support six associations: , , , && . belongs_to has_one has_many has_many :through, has_one :through has_and_belongs_to_many 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: < ApplicationRecord class User # write your association staff here end < ApplicationRecord class Post # write your association staff here end User can have zero{ or multiple Posts instances. nil} 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. one-to-many has_many belongs_to #1 In your model's folder, open and file. Add the following methods. user.rb post.rb posts < ApplicationRecord class User # write your association staff here has_many: end user < ApplicationRecord class Post # write your association staff here belongs_to: end #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. db/migrate/time_stamp_add_UserId_to_post.rb add_column , , add_index , < ActiveRecord::Migration[5.2] class AddUserIdToPosts def change :posts :user_id :string :posts :user_id end end This migration is pretty straightforward. It adds a column to the table and also sets the column as the pointing to the user instance{author}. Run to persist the changes. :user_id :posts user_id foreign key rails db:migrate 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 rails console Run to apply recent changes. reload! reload! Create a new user: ➜ new_user = User.create( , , , ) ➜ new_user.save first_name: "John" last_name: "Doe" email: "example@gmail.com" password: "password" Create two new posts, authored by the above user. ➜ user = User.first ➜ new_post1 = user.posts.build( ) ➜ new_post2 = user.posts.build( ) ➜ new_post1.save ➜ new_post2.save body: "This is my first Article, please give it a star" body: "This is my second Article, please give it a star if you like" Access all the posts authored by John Doe ➜ user = User.where( , ) ➜ user.posts "first_name = ?" "John" Access the author of a Post: p1 = Post.first p1.user Congratulations!!! That is it for a one-to-many relationship. Be sure to visit Rails for a better understanding. docs Many-to-many: Association 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. Friendships Users can send and receive friend requests from other users. Users can multiple friends. Users can accept friend requests as well as reject any sort of invitation. We need to delete a friendship/invitation record upon rejection/unfriend action from the user. 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. users 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. Action #1 Scaffold the Friendship model rails g model Friendship references references bolean user: friend: confirmed: 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. references 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. create_table t.references , t.references , t.boolean t.timestamps add_foreing_key , , < ActiveRecord::Migration[5.2] class CreateFriendships def change :friendships do |t| :user foreign_key: true :friend index: true :confirmed end :friendships :users column: :friend_id end end using the method above allows us to reference the user table as a friends table. Go ahead and perform the data migrations. add_foreing_key rails db:migrate #2 Update the Relationships on the Friendship model Edit the Friendship class to be similar to this. => end < : : , : class Friendship ApplicationRecord belongs_to user belongs_to friend class_name 'User' points the table to the table since a friend is also a user. Belongs_to :friend, :class_name => 'User' friends users #3 Update the Relationship in the User Model => , : end < : : : , : 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. direct_friends = friendships.map { friendship.friend friendship.confirmed } inverse_friends = reciprocal_friends.map { friendship.user friendship.confirmed } (direct_friends + inverse_friends).compact def friends |friendship| if |friendship| if end Method 2: Keep track of pending invitations. friendships.map { friendship.friend friendship.confirmed }.compact # pending invitations def pending_friend_requests |friendship| unless end Method 3: Incoming friend Requests. # incoming friend requests def incoming_friend_requests reciprocal_friends.map { |friendship| friendship.user unless friendship.confirmed }.compact end Method 4: Confirm Friend Requests def confirm_friend_request?(user) friend = reciprocal_friends.find { |friendship| friendship.user == user } friend friend.confirmed = friend.save end end if true true else false Method 5: Check whether a user is already a friend. friends. ?(user) def is_friend? (user) include end 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 and part of the associations. comments likes