Reputation: 861
I'm using Rails 4.1 and have a very basic problem getting eager loading to work.
I've simplified a complex program into a very basic example of events, users and reservations:
The event
model:
class Event < ActiveRecord::Base
has_many :reservations
def reserved?(user)
reservations.where(user: user).present?
end
end
The user
model:
class User < ActiveRecord::Base
has_many :reservations
end
The reservation
model:
class Reservation < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
My event controller
:
def index
@events = Event.includes(:reservations).all
@user = User.find(1)
end
My views/events/index.html.erb
view:
<% @events.each do |event| %>
<p>Event: <%= event.name %><br>
Reserved?: <%= event.reserved?(@user) %><br></p>
<% end %>
I've created a user and three events. The resulting server log
for the event index page:
Started GET "/events" for 127.0.0.1 at 2014-12-03 11:26:20 +0800
Processing by EventsController#index as HTML
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
Event Load (0.1ms) SELECT "events".* FROM "events"
Reservation Load (0.2ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."event_id" IN (1, 2, 3)
Reservation Load (0.1ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1 [["event_id", 1]]
Reservation Load (0.0ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1 [["event_id", 2]]
Reservation Load (0.1ms) SELECT "reservations".* FROM "reservations" WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1 [["event_id", 3]]
Rendered events/index.html.erb within layouts/application (10.0ms)
Completed 200 OK in 76ms (Views: 68.7ms | ActiveRecord: 1.3ms)
As you can see, rails correctly eager loads all the reservations for my events. However it then proceeds to query the database again during the reserved?(user)
call. What am I doing wrong? Thanks.
Upvotes: 2
Views: 1120
Reputation: 558
Your reserved?
method uses the where
method of the reservations
association. The where
method is an ARel method that is used to build SQL queries. Calling present?
on the result of the where
method causes the query to be executed.
To avoid doing another database lookup in reserved?
, you should use a Ruby collection method instead, for example:
def reserved?(user)
reservations.select { |reservation| reservation.user == user }.empty?
end
Or, for a more concise version (someone added this as a comment, but it looks like it may have been deleted):
def reserved?(user)
reservations.map(&:user_id).include?(user.id)
end
Upvotes: 3