Forum: Ruby on Rails Avoid reloading parent of association?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
dasil003 (Guest)
on 2007-06-12 00:09
(Received via mailing list)
Consider this situation

class Property < ActiveRecord::Base
  has_many :photos
end

Now if you reference:

@property.photos[0].property

It reloads the property.  I'm looking for a way to suggest the
association directly to the associated objects.

I know this example seems contrived, but it's just a simplification.
In actuality my Photo model has a method that looks like this:

class Photo < ActiveRecord::Base
  def public_filename
    File.join("images", property.idx.name, filename)
  end
end

Not only does this result in n+1 queries, but it actually results in 2n
+1, even though I am eager loading all the necessary data up front.

I can think of a lot of ways to solve this, but to cleanly solve it
requires somehow suggesting the existing loaded @property object to
the photo.property assocation.  If there's no way to do this, maybe
there should be...
Mark Reginald James (Guest)
on 2007-06-12 06:24
(Received via mailing list)
dasil003 wrote:
> It reloads the property.  I'm looking for a way to suggest the
>
> Not only does this result in n+1 queries, but it actually results in 2n
> +1, even though I am eager loading all the necessary data up front.
>
> I can think of a lot of ways to solve this, but to cleanly solve it
> requires somehow suggesting the existing loaded @property object to
> the photo.property assocation.  If there's no way to do this, maybe
> there should be...

Yes, back associations aren't currently found and set automatically.
You can have it done automatically for your association:

class Property < ActiveRecord::Base
   has_many :photos
   def photos_with_back_links(*params)
     photos_without_back_links(*params).each { |p| p.property = self }
   end
   alias_method_chain :photos, :back_links
end

--
We develop, watch us RoR, in numbers too big to ignore.
dasil003 (Guest)
on 2007-06-14 00:57
(Received via mailing list)
Very cool solution, looking up alias_method_chain was well worth it.

There is one problem when calling a method on the association proxy
that doesn't actually return an array of Photos such as:

@property.photos.count

I dug into AssocationProxy and it's subclasses to figure out how best
to make this work.  I think something like:

class Property < ActiveRecord::Base
  has_many :photos
  def photos_with_back_links(*params)
    result = photos_without_back_links(*params)
    result.each { |p| p.set_property_target = self } if result.is_a?
(Array)
  end
  alias_method_chain :photos, :back_links
end

will work generally, although it could still choke on methods that
return an array that's not a list of photos... is it worth putting a
conditional inside the loop?
dasil003 (Guest)
on 2007-06-14 01:34
(Received via mailing list)
Actually the other methods don't work at all.  This technique can't
really solve the problem satisfactorily because the array isn't loaded
until a method is called on the proxy that needs it.  This alias-
chaining is too early.

The method to chain is find_target in AssociationCollection, but that
is considerably trickier to pull off...
Trevor Squires (Guest)
on 2007-06-14 01:56
(Received via mailing list)
class Property < ActiveRecord::Base
  has_many :photos do
    def hydrate
      each {|x| x.set_property_target @owner}
    end
  end
end

Property.find(:all, :limit => 10, :include => :photos).each {|x|
x.photos.hydrate}

Trevor

On 6/13/07, dasil003 <gabriel.d@gmail.com> wrote:
> > Very cool solution, looking up alias_method_chain was well worth it.
> >   has_many :photos
> > conditional inside the loop?
> > > > Now if you reference:
> > > >   def public_filename
> > > > there should be...
> > > end
> >
> > > --
> > > We develop, watch us RoR, in numbers too big to ignore.
>
>
> >
>


--
--
Trevor Squires
http://somethinglearned.com
Mark Reginald James (Guest)
on 2007-06-14 06:32
(Received via mailing list)
dasil003 wrote:
> Actually the other methods don't work at all.  This technique can't
> really solve the problem satisfactorily because the array isn't loaded
> until a method is called on the proxy that needs it.  This alias-
> chaining is too early.

Yes, I'd only considered the case where the unadorned whole collection
was being fetched, not the use of proxy methods. I now have a better
understanding of the delayed loading the proxy implements by defining
the respond_to? method.

The "each" triggers the load of the whole association, so rather
than aliasing the association name you'd have to have a separate
method for the back-linked version, only used when you want the
whole collection.

--
We develop, watch us RoR, in numbers too big to ignore.
This topic is locked and can not be replied to.