Find Through Association

Steps

Step 1

Let's create a project resource.

rails g scaffold project name:string

Step 2

Define the has_many ActiveRecord association in the project model.

class Project < ActiveRecord::Base
  has_many :tasks
end

Step 3

Define the belongs_to ActiveRecord association in the task model.

class Task < ActiveRecord::Base
  belongs_to :project
end

Step 4

Task belongs to a project. So add the foreign key project_id to the tasks table. Create the migration.

rails g migration add_project_id_to_tasks

Add the column in the migration file.

class AddProjectIdToTasks < ActiveRecord::Migration
  def change
    add_column :tasks, :project_id, :integer
  end
end

Step 5

Migrate the database.

rake db:migrate

Step 6

In seeds.rb, add:

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)

Step 7

Run:

rake db:seed

to populate the data.

Step 8

Let's now play in the rails console.

  Project.count
   (0.1ms)  SELECT COUNT(*) FROM "projects"
 => 2 

There are two projects.

  Task.count
   (0.1ms)  SELECT COUNT(*) FROM "tasks"
 => 4 

There are 4 tasks. Let's find the incomplete tasks for the first project.

 x = Task.where(project_id: 1).where(complete: false)
  Task Load (0.3ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  [["project_id", 1], ["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>]> 

We get ActiveRecord::Relation object as the result. We can find the first project.

  p = Project.find 1
   Project Load (0.2ms)  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"> 

Now we can use the association to make the query.

  p.tasks.where(complete: false).to_a
  Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  [["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>] 

To force the database query and get the results we use to_a. There is one task that belongs to first project that is incomplete. If we don't use to_a method:

  p.tasks.where(complete: false)
   Task Load (0.1ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ? AND "tasks"."complete" = ?  [["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 get ActiveRecord::AssociationRelation object as the result. In Rails 4.2, we no longer have find_all_by_some_column(value) available.

 > p.tasks.find_all_by_complete(false)
 NoMethodError:   Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" = ?  [["project_id", 1]]
 undefined method `find_all_by_complete' for #<Task::ActiveRecord_Associations_CollectionProxy:0x007fe597001fe8>
 Did you mean?  find_or_create_by

Summary

In this article, you learned how to find records through the ActiveRecord association.


Related Articles


Create your own user feedback survey