Ask A Question

Notifications

You’re not receiving notifications from this thread.

How do I use includes to return all columns of associated table?

Oliver asked in Rails

I hope someone here can help me. There's a lot to ActiveRecord I'm yet to understand and I'm a little lost at the moment.

I have three models. Issue, Language, Labels.

An issue has a language and many labels. Languages and labels have many issues.

Because issues and labels have a many to many relationship, I have created a join table tags. (I suppose I could chose a more appropriate name.)

class Label < ApplicationRecord
  has_many :tags
  has_many :issues, through: :tags
end

class Issue < ApplicationRecord
  has_many :tags
  has_many :labels, through: :tags
  belongs_to :language
  ...
end

class Tag < ApplicationRecord
  belongs_to :issue
  belongs_to :label
end

I want to be able to filter issues by label or language. To that end, I've added two scopes to Issue.

scope :by_labels, lambda { |labels|
  joins(:labels).where(labels: { name: labels })
}

scope :by_language, lambda { |language|
  joins(:language).where(languages: { name: language })
}

I can search for an issue by a single label like so:

> Issue.by_labels('bug').first.labels.map(&:name)
=> ["bug", "help wanted"]

Now, in my controller I want to eager load labels so not to end up with N+1 queries. I use includes:

  @issues = Issue.by_labels('bug').includes(:labels, :language)

However, I now see that only the label specified in the where query is returned:

> Issue.by_labels('bug').includes(:labels, :language).first.labels.map(&:name)
=> ["bug"]

I need each issue row to point to all of its labels. ["bug", "help wanted"] not ["bug"].

Why is it returning the single label? What is includes doing that it doesn't return all the labels?

The executed SQL is:

  SELECT "issues"."id" AS t0_r0, "issues"."assigned" AS t0_r1,
  "issues"."description" AS t0_r2, "issues"."repo_name" AS t0_r3,
  "issues"."title" AS t0_r4, "issues"."url" AS t0_r5,
  "issues"."user_avatar_url" AS t0_r6, "issues"."created_at" AS t0_r7,
  "issues"."updated_at" AS t0_r8, "issues"."language_id" AS t0_r9,
  "labels"."id" AS t1_r0, "labels"."name" AS t1_r1, "labels"."issue_id" AS
  t1_r2, "labels"."created_at" AS t1_r3, "labels"."updated_at" AS t1_r4,
  "languages"."id" AS t2_r0, "languages"."name" AS t2_r1,
  "languages"."color" AS t2_r2, "languages"."created_at" AS t2_r3,
  "languages"."updated_at" AS t2_r4 FROM "issues" INNER JOIN "tags" ON
  "tags"."issue_id" = "issues"."id" INNER JOIN "labels" ON "labels"."id" =
  "tags"."label_id" LEFT OUTER JOIN "languages" ON "languages"."id" =
  "issues"."language_id" WHERE "labels"."name" = ? ORDER BY
  "issues"."created_at" DESC  [["name", "bug"]]

What should I be doing to ensure each issue is returned with all of its corresponding labels?

Any help would be appreciated.

Reply

I don't quite know why it works but passing a string to includes instead of a symbol returns both labels.

> Issue.by_labels('bug').includes('labels', 'language').first.labels.map(&:name)
=> ["bug", "help wanted"]
Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 84,111+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.