Rails: Hack belongs_to to allow :select

[ Posted by kevin Tue, 17 Apr 2007 22:57:52 GMT ]

For some reason most rails associations allow :select as a parameter to limit which rows are loaded, but belongs_to doesn't.

Here's my (only rudimentarily tested) monkey-patch to add it:

module ActiveRecord::Associations::ClassMethods
  def create_belongs_to_reflection(association_id, options)
    $stderr.puts "MAKING REFLECTION"
    options.assert_valid_keys(
      :select, #added
      :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent, 
      :counter_cache, :extend, :polymorphic
    )
    
    reflection = create_reflection(:belongs_to, association_id, options, self)

    if options[:polymorphic]
      reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
    end

    reflection
  end
end
module ActiveRecord
  module Associations
    class BelongsToAssociation  @reflection.options[:select] || '*', #added
          :conditions => conditions,
          :include    => @reflection.options[:include]
        )
      end
    end
  end
end

Technorati Tags: ,

Posted in  | 9 comments

Rails: How to eager load associations with :select (only load select columns from each table)

[ Posted by kevin Fri, 13 Apr 2007 20:51:29 GMT ]

I've been avoiding blogging about things that were overly technical for awhile, but screw it, I need more things to write about anyways, so I'll start sharing nuggets of knowledge here as well.

Rails eager loading lets you load sub-elements on the database level, which can be a huge speed increase, though sometimes at a database cost. In my experience, the biggest problem is that items I'm joining together often have a big 'description' column, that can cause memory issues if loaded too indescriminatingly.

@logs = ItemLog.find(:all, :conditions => "target_id=#{@user.id}", :order => 'item_logs.created_at DESC', :limit => 40, :include => 'item')

What I wanted to do was add :select => 'only_rows_i_want'. But rails doesn't let you use :select with :include.

So what I did was do it without the :select, and take a look at the log file to see what the generated query would be. Then I removed the items I wanted and used it as a find_by_sql query:

rows = ItemLog.find_by_sql("SELECT item_logs.`id` AS t0_r0, item_logs.`user_id` AS t0_r1, item_logs.`item_id` AS t0_r2, item_logs.`created_at` AS t0_r3, item_logs.`referer` AS t0_r4, item_logs.`target_id` AS t0_r5, items.`id` AS t1_r0, items.`user_id` AS t1_r1, items.`title` AS t1_r4 FROM item_logs LEFT OUTER JOIN items ON items.id = item_logs.item_id WHERE (target_id=#{@user.id}) ORDER BY item_logs.created_at DESC LIMIT 50")

Then how do you get it to make the sub-elements into the actual objects?

join_dep = ActiveRecord::Associations::ClassMethods::JoinDependency.new(ItemLog, ['item'], nil)
@logs = join_dep.instantiate(rows)

I've put the name of the class I'm doing the find on as the first parameter, the include field I used as the 2nd. It works great with rails 1.2!

Technorati Tags: , ,

Posted in  | 4 comments