Preventing n+1 queries due to queries triggered in view templates
I am generally trying to prepare as much data as possible in the controller, so that my views do not trigger additional queries, possibly causing n+1 queries in the process.
I just encountered the following case:
An index of students
@students = Roles::Student.includes(:user, enrollments: :course).all
gets iterated over in the index view
<%= turbo_frame_tag "students", target: "_top" do %>
<ul role="list">
<%= render partial: "students/student", collection: @students %>
</ul>
<% end %>
with the help of this partial
<li id="<%= dom_id student %>">
<...>
<% student.current_enrollments.each do |enrollment| %>
<div>
<%= enrollment.course_full_title %>
</div>
<% end %>
</li>
There are two issues i encountered:
student.current_enrollments
def current_enrollments
enrollments.active
end
Sending current_enrollments to an instance of students sends in turn the scope active to its associated enrollments, causing a query. As the view iterates over all students, this is a typical n+1 case.
As index shows, i do an includes on enrollments, and my assumption was that internally rails would direct active onto that relation proxy.
What i ended up doing was filtering the available eagerly loaded enrollments:
def current_enrollments
enrollments.reject { |enrollment| !enrollment.state.in?(["registered", "confirmed", "waiting"]) }
end
however, this is tedious to maintain, and a reason why i came up with the active scope in the first place.
Is there an alternative? Or is my architecture Just Crap™?
Turns out
Hey! I'll happily receive your comments
via email.
Thanks for reading.