Force Mapping on a Model Instance

Consider this:

class Post < ActiveRecord::Base
  has_many :comments

  def last_comment
    comments.last
  end
end

railsconsole> p = Post.last
  Post Load (0.2ms)  SELECT  `posts`.* FROM `posts`  WHERE ORDER BY `posts`.`id` DESC LIMIT 1
railsconsole> p.last_message
  Post Load (0.4ms)  SELECT `comments`.* FROM `comments`  WHERE `messages`.`post_id` = 14
railsconsole> p.last_message
  Post Load (0.4ms)  SELECT `comments`.* FROM `comments`  WHERE `messages`.`post_id` = 14

      

You might think that there should only be 2 queries here: an initial find and then loading the association. Subsequent calls to the association must be cached. However, it is not. The association is never loaded or cached because Rails tries to be smart and only loads the last entry. And since Rails doesn't keep track of the last entry (or the first or any other optional custom request in the association), it just returns the response every time.

However, if you want to cache the link? I've searched SO and couldn't find a direct answer to this question.

+3


source to share


2 answers


When you call a connection to an ActiveRecord instance, you return a proxy object, and in the case of a has_many object, you get a CollectionProxy.

What's odd is that you can call #load on the collection proxy, but that doesn't load the association cache.

Hidden in the Rails docs: http://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-load_target

#load_target

      



|

class Post < ActiveRecord::Base
  has_many :comments

  def last_comment
    comments.load_target unless comments.loaded?
    comments.last
  end
end

      

Hope others find this helpful.

+4


source


I would make last_comment an association

has_one :last_comment, -> { order 'created_at DESC' }, class_name: 'Comment'

      



This way you can include, join or eager_load too.

+2


source







All Articles