Preloading Nested Active Record Associations Syntax
Published on 2025-04-21
I recently opened a Pull Request where I was including nested associations in an Active Record query and received feedback that syntax was unfamiliar. It hadn't crossed my mind that this syntax would be unfamiliar to others so I wrote up an explanation in a comment about what was happening, the Pull Request was approved and the feature was shipped. However, that got me thinking that I would like to share the syntax and do a little exploration as to why it might have been unfamiliar to others. The query in question looked something like this.
The query syntax in question#
If this looks unfamiliar to you, let's set up the context.
We have three models in a three level hierarchy where Organization
is at the top, Team
is in the middle, and User
is at the bottom. The syntax of the query is saying for all the User
records we look up, we also want to load
the associated Team
record as well as the Organization
that the Team
belongs to. This loading of associated
records is the resolution to the classic N+1 problem where it could be possible to hit the database multiple times
as we loop through each User
record. Let's look at a quick example to clarify the N+1 problem.
Now that we understand what the syntax is doing and why it is useful, let's explore why it might be unfamiliar.
Why is this syntax unfamiliar?#
I think the reason this syntax is unfamiliar is because it isn't mentioned in the
Active Record Query Interface documentation. The
documentation has many examples of using .includes
and I picked out the three different types to show here.
Let's break down what each of these are doing.
This example showcases that a Book
is related to an author
via a belongs_to
or has_one
association.
This example showcases that a Customer
has two relationships, has_many :orders
and has_many :reviews
. We're
preloading both associations so that we don't have to make additional trips to the database to later load the data.
Note that this isn't an N+1 example, these two relationships load lists and can be all fetched at once. This example
could also be written like this.
The change to using array syntax is minor, but it demonstrates that associations that belong to the model can be encapsulated into an array. We see this syntax put to work in the final example.
This example builds on the previous in a more nested manner. We now see that the Order
class has_many :books
and
each Book
is related to a supplier
and an author
via a belongs_to
or has_one
association. We know that
we can encapsulate associations to the same model through the array syntax and that approach is used here to
preload multiple associations to Book
.
Now that we understand different approaches to the preloading syntax, let's revisit the syntax of our original query and rewrite it to match the documentation.
The original query is omitting the array syntax around :organization
because we're only loading a single
association and that is perfectly valid syntax!
Conclusion#
I found an answer to a question on Stack Overflow, Rails - Nested includes on Active Records? from 2014 that may be the source of where I learned this syntax. The answer shows some examples of how to build up a structure of extremely nested associations which shouldn't look unfamiliar now that we've covered the basics here. Hopefully this helps the next time you're writing or reviewing some Active Record code!