Using set_association_target to Avoid Reloading ActiveRecord Associations
June 12, 2007
Being smart with ActiveRecord means making sure your code is at least passably efficient with the database. Consider the following code:
class Thing < ActiveRecord::Base
has_many :photosendclass Photo < ActiveRecord::Base
belongs_to :thingdef public_filename
File.join("images",thing.name,filename)
endend@thing= Thing.find(:first, :include => :photos)
@thing.photos[0].public_filename #results in an extra query
You might assume that all of of @@thing@’s photos would automatically have their belongs_to association set when they are accessed through @thing.photos. After all, @thing is right there, there wouldn’t be any extra cost. Unfortunately that does not happen. I’ve dug through the ActiveRecord association code a bit to understand this issue. I’m thinking a fix for this at the core level might be computationally costly, which would explain this behavior. However I’m not yet familiar enough with the ActiveRecord code to quickly analyze the whole situation, much less create a patch.
set_association_target to the rescue
Fortunately I did discover an undocumented public method that allows you to solve the problem easily—if not prettily—in practice. The method is set_association_target where association is replaced by the name of your association. It’s actually pretty similar to association= except that it doesn’t modify anything, it just attaches the object directly to the association, which means that you better know what you are doing when you use it. But since when has Ruby ever assumed you don’t know what you’re doing?
So the fix is:
@thing= Thing.find(:first, :include => :photos)
@thing.photos[0].set_thing_target(@thing)
@thing.photos[0].public_filename #no extra query.
It seems like ActiveRecord ought to be able to figure this out for itself, but for whatever reason, it doesn’t. Sure you can work around the limitation by defining the public_filename method on Thing instead of Photo, but that feels like it negatively affects the whole architecture rather than just uglifying a small snippet. At least that’s my opinion.
Using set_association_target to Avoid Reloading ActiveRecord Associations
Being smart with ActiveRecord means making sure your code is at least passably efficient with the database. Consider the following code:
You might assume that all of of @@thing@’s photos would automatically have their belongs_to association set when they are accessed through
@thing.photos
. After all,@thing
is right there, there wouldn’t be any extra cost. Unfortunately that does not happen. I’ve dug through the ActiveRecord association code a bit to understand this issue. I’m thinking a fix for this at the core level might be computationally costly, which would explain this behavior. However I’m not yet familiar enough with the ActiveRecord code to quickly analyze the whole situation, much less create a patch.set_association_target
to the rescueFortunately I did discover an undocumented public method that allows you to solve the problem easily—if not prettily—in practice. The method is
set_association_target
whereassociation
is replaced by the name of your association. It’s actually pretty similar toassociation=
except that it doesn’t modify anything, it just attaches the object directly to the association, which means that you better know what you are doing when you use it. But since when has Ruby ever assumed you don’t know what you’re doing?So the fix is:
It seems like ActiveRecord ought to be able to figure this out for itself, but for whatever reason, it doesn’t. Sure you can work around the limitation by defining the
public_filename
method onThing
instead ofPhoto
, but that feels like it negatively affects the whole architecture rather than just uglifying a small snippet. At least that’s my opinion.