Rails 3 Eager Download with conditions - how to access the downloaded data?

I have many cases in my application when a user has no more than one object (say, "description") within its connection with another object ("group").

For instance:

class User < ActiveRecord::Base has_many :descriptions has_many :groups class Group < ActiveRecord::Base has_many :users has_many :descriptions class Description < ActiveRecord::Base belongs_to :user belongs_to :group 

If I wanted to display all users in a specific group and include their respective descriptions, I could do something like the following:

 #users model def description_for(group_id) descriptions.find_by_group_id(group_id) end #view @group.users.each do |user| user.name user.description_for(@group.id).content 

But this generates a huge number of description requests. I tried using connections:

 #controller @group = Group.find(params[:id], :joins => [{:users => :descriptions}], :conditions => ["descriptions.group_id = ?", params[:id]]) 

But since I still call user.description_for (@ group.id), this does not help when loading the page.

UPDATE: Sample Generated SQL

 Rendered users/_title.html.haml (1.6ms) CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1 User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 7 LIMIT 1 CACHE (0.0ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1 Description Load (0.1ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 7 AND "descriptions"."group_id" = 28 LIMIT 1 CACHE (0.0ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 7 AND "descriptions"."group_id" = 28 LIMIT 1 Rendered users/_title.html.haml (1.7ms) CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1 User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 51 LIMIT 1 CACHE (0.0ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1 Description Load (0.1ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 51 AND "descriptions"."group_id" = 28 LIMIT 1 CACHE (0.0ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 51 AND "descriptions"."group_id" = 28 LIMIT 1 Rendered users/_title.html.haml (1.8ms) CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1 User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 5 LIMIT 1 CACHE (0.0ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1 Description Load (0.1ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 5 AND "descriptions"."group_id" = 28 LIMIT 1 CACHE (0.0ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 5 AND "descriptions"."group_id" = 28 LIMIT 1 Rendered users/_title.html.haml (1.7ms) CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1 User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 52 LIMIT 1 CACHE (0.0ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1 Description Load (0.2ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 52 AND "descriptions"."group_id" = 28 LIMIT 1 CACHE (0.0ms) SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 52 AND "descriptions"."group_id" = 28 LIMIT 1 Rendered users/_title.html.haml (1.7ms) 
+4
source share
2 answers

That's right, I think that you really do not need the joins clause in rails 3. If you use include and where, Arel will do the hard work for you.

I tested this (although I used a different set of models (and attributes) than yours) using models with the same association location, and I think this should work:

in models /user.rb:

 scope :with_group_and descriptions, lambda { |group_id| includes(:groups, :descriptions).where(:groups => { :id => group_id }, :descriptions => { :group_id => group_id }) } 

Then in the controller you call:

 @users = User.with_group_and_descriptions(params[:id]) 

Finally, in the view, you can:

 @users.each do |user| user.name user.descriptions.each do |desc| desc.content # or @users.each do |user| user.name user.descriptions[0].content 

If I understood correctly, this should only make 2 dB of calls. One to get the list of user_ids, and the second to get data about the user, group and description, and even though you call the method of describing user objects, which usually contains all the descriptions (and not just those that belong to a specific group), since you already filled the union rails, you won’t be able to capture all the associations again when you call user.descriptions , instead it will simply display the ones you pulled from the database using the description of .group_id, where is the item. Calling user.descriptions(true) will reload the descriptions, which will return an array of all description associations for the user.

+1
source

Take a look at include - it indicates an association that should be loaded with impatience.

-1
source

All Articles