Rails 6 adds Relation#extract_associated


Rails 6 has added ActiveRecord::Relation#extract_associated for extracting associated records from a relation.

Before

Trying to access collections from related collections on objects, is a common use case in many applications. A simple example is if we want to fetch all authors of posts from a given category. Previously, if we wanted to extract such associated records from a relation we would make use of preload and collect.

> category.posts.preload(:author).collect(&:author)
Post Load (4.8ms)  SELECT "posts".* FROM "posts" WHERE "posts"."category_id" = ?  [["category_id", 2]]
Author Load (3.5ms)  SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (?, ?, ?)  [["id", 1], ["id", 2], ["id", 3]]
=> [#<Author id: 1, name: "David", created_at: "2019-08-16 06:26:29", updated_at: "2019-08-16 06:26:29">, #<Author id: 2, ...]

This usage of preload(:author).collect(&:author) can get repetitive and does not describe the intent of usage properly.

extract_associated method

Relation#extract_associated has now been introduced as a shorthand for preload and collect. It extracts a named association from the relation. The named association is first preloaded, then the individual association records are collected from the relation.

> category.posts.extract_associated(:author)
Post Load (0.2ms)  SELECT "posts".* FROM "posts" WHERE "posts"."category_id" = ?  [["category_id", 2]]
Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (?, ?, ?)  [["id", 1], ["id", 2], ["id", 3]]
=> [#<Author id: 1, name: "Sam", created_at: "2019-08-16 06:26:29", updated_at: "2019-08-16 06:26:29">, #<Author id: 2, name: "Matt", created_at: "2019-08-16 06:26:32", updated_at: "2019-08-16 06:26:32">, #<Author id: 3, name: "John", created_at: "2019-08-16 06:26:35", updated_at: "2019-08-16 06:26:35">]

extract_associated returns collection of associated collections in case of belongs_to and has_many associations.

> category.posts.extract_associated(:comments)
Post Load (0.2ms)  SELECT "posts".* FROM "posts" WHERE "posts"."category_id" = ?  [["category_id", 2]]
Comment Load (0.6ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" IN (?, ?, ?)  [["post_id", 1], ["post_id", 2], ["post_id", 3]]
=> [#<ActiveRecord::Associations::CollectionProxy [#<Comment id: 7, post_id: 1, description: "Comment 1", created_at: "2019-08-16 06:29:09", updated_at: "2019-08-16 06:29:09">, #<Comment id: 8,...]>]