Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Active Record: Eager load association with parameter

I have the following models:

class Rating < ActiveRecord::Base
  belongs_to :item
  belongs_to :user
end

class Item < ActiveRecord::Base
  has_many :ratings
end

I want to fetch all items, and the ratings made by a specific user to show the current user's rating (if it exists!) next to each item.

I've tried...

Item.includes(:ratings).where('ratings.user_id = ?', user_id)

...but that don't give me the items with no ratings.

My first thought was a has_many association with an argument, and then pass that argument with the includes method. But that doesn't seem to exist.

How do I get all posts and eager loaded association filtered on a parameter without doing N+1 queries or loading all entities into memory?

like image 229
thejaz Avatar asked Mar 21 '13 08:03

thejaz


2 Answers

Step 1

Make the current_user accessible in the model layer (one technique is outlined here: https://stackoverflow.com/a/2513456/163203)

Step 2

Add an association with a condition that gets evaluated during run time.

Rails 2

class Item < ActiveRecord::Base
  has_many :ratings
  has_many :current_user_ratings, 
           :class_name => "Rating", 
           :conditions => 'ratings.user_id = #{User.current_user.try(:id)}'
end

Rails 3.1 and above

class Item < ActiveRecord::Base
  has_many :ratings
  has_many :current_user_ratings, 
           :class_name => "Rating", 
           :conditions => proc { ["ratings.user_id = ?", User.current_user.try(:id)] }
end

Step 3

Item.includes(:current_user_ratings)
like image 84
Harish Shetty Avatar answered Sep 23 '22 00:09

Harish Shetty


As I recently wrote in this blog post, I'd suggest the following in your case:

items = Item.all
ActiveRecord::Associations::Preloader.new.preload(items, :ratings, Rating.where(user_id: user_id))

you can use the custom scopes of the preloader and access items.each { |i| i.ratings } already scoped by the user.

like image 20
coorasse Avatar answered Sep 23 '22 00:09

coorasse