Rails 4.2 ActiveRecord Basics

Sample Data

Change the seeds.rb:

wealthy = Project.create(name: 'Wealth Building')
wealthy.tasks.create(name: 'Get rich quick', complete: false)
wealthy.tasks.create(name: 'Write a book', complete: true)

happy = Project.create(name: 'Be Happy')
happy.tasks.create(name: 'Buy a puppy', complete: false)
happy.tasks.create(name: 'Dance in the rain', complete: true)

Run rake db:seed to populate the database.

Define the Relationships

Define the has_many relationship in the Project.

class Project < ActiveRecord::Base
  has_many :tasks
end

Define the belongs_to in the task.

class Task < ActiveRecord::Base
  belongs_to :project
end

Now we can play with our database.

Chaining Methods

We can find all incomplete tasks ordered by created_at in descending order.

 > Task.where(complete: false).order('created_at DESC')
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  ORDER BY created_at DESC  [["complete", "f"]]
 => #<ActiveRecord::Relation [#<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>, #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]> 

It is better to use id column to order for better performance. The primary key is already indexed and it is faster to sort records by using id. This becomes important when you are dealing with lot of records. We can use id for sorting like this:

 > Task.where(complete: false).order('id DESC')
   Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  ORDER BY id DESC  [["complete", "f"]]
  => #<ActiveRecord::Relation [#<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>, #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]>

If you don't use the sorting, by default sorting shows the oldest records first.

  > Task.where(complete: false)
   Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  [["complete", "f"]]
  => #<ActiveRecord::Relation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>]>

Using Associations to Find Records

A project has many tasks. Let's find a project first.

 p = Project.find 1
  Project Load (0.1ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT 1  [["id", 1]]
 => #<Project id: 1, name: "Wealth Building", created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20"> 

We can now find incomplete tasks for this project sorted by id in descending order.

 p.tasks.where(complete: false).order('id DESC')
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  ORDER BY id DESC  [["project_id", 1], ["complete", "f"]]
 => #<ActiveRecord::AssociationRelation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]>

This returns ActiveRelation object. Can we retrievel all the records?

 > p.tasks.where(complete: false).order('id DESC').all
  Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  ORDER BY id DESC  [["project_id", 1], ["complete", "f"]]
 => #<ActiveRecord::AssociationRelation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]>

We still get ActiveRelation object. We can use to_a method:

> p.tasks.where(complete: false).order('id DESC').to_a
  Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  ORDER BY id DESC  [["project_id", 1], ["complete", "f"]]
 => [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]

Now we have all the incomplete tasks for this project sorted in descending order.

Move Finders to Model

We can move the finders to the model. In Task:

 class Task < ActiveRecord::Base
   belongs_to :project

   def self.find_incomplete
      where(complete: false).order('id DESC')
   end
 end

We can call this method in the tasks controller:

def index
  @tasks = Task.find_incomplete
end

def show
  @project = Project.find(params[:id])
  @tasks = @project.tasks.find_incomplete
end

Let's find a project:

 p = Project.find 1
  Project Load (0.6ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT 1  [["id", 1]]
 => #<Project id: 1, name: "Wealth Building", created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20"> 

Let's find all the tasks for this project:

 > p.tasks
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ?  [["project_id", 1]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>, #<Task id: 2, name: "Write a book", complete: true, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]> 

We can now use find_incomplete method to find only the incomplete tasks:

 > p.tasks.find_incomplete
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  ORDER BY id DESC  [["project_id", 1], ["complete", "f"]]
 => #<ActiveRecord::AssociationRelation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]>

Scopes

Let's define scopes for incomplete tasks and sorted tasks.

class Task < ActiveRecord::Base
  belongs_to :project

  scope :incomplete, -> { where complete: false }
  scope :sorted, -> { order('id DESC') }

  def self.find_incomplete
     where(complete: false).order('id DESC')
  end
end

We can now play with this task in the rails console. We can retrieve all incomplete tasks:

 > Task.incomplete
  Task Load (0.3ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  [["complete", "f"]]
 => #<ActiveRecord::Relation [#<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>]> 

We can sort all tasks:

 > Task.sorted
  Task Load (0.1ms)  SELECT "tasks".* FROM "tasks"  ORDER BY id DESC
 => #<ActiveRecord::Relation [#<Task id: 4, name: "Dance in the rain", complete: true, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>, #<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>, #<Task id: 2, name: "Write a book", complete: true, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>, #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]> 

Merging of scopes

Just like where clauses scopes are merged using AND conditions. We can mix and match scope and where conditions and the final sql will have all conditions joined with AND.

 > Task.incomplete.sorted
  Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."complete" = ?  ORDER BY id DESC  [["complete", "f"]]
 => #<ActiveRecord::Relation [#<Task id: 3, name: "Buy a puppy", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 2>, #<Task id: 1, name: "Get rich quick", complete: false, created_at: "2016-03-04 23:50:20", updated_at: "2016-03-04 23:50:20", project_id: 1>]> 

You can download the source code for this article from: https://github.com/bparanj/doer.

Summary

In this article, you learned how to retrieve records using ActiveRelation object, use scopes and use merged scopes.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.