Search code examples
sqlruby-on-railsruby-on-rails-3arel

How can I eager load the most recent object in a has_many relationship?


I have a rails 3 app that has models like categories and posts.

Category

has_many :posts

Post

belongs_to :category

I want to be able to display a list of categories with the most recent post listed next to it without looping through and hitting the database for each category and only pulling back one post per category.

I want to do the same thing this question poses - SQL join: selecting the last records in a one-to-many relationship - but hopefully using Active Record.

Is this a dumb thing to be doing? Should I just pull back all the posts for a category by eager loading them? I was concerned about performance, which is why I started researching this.


Solution

  • I'm not sure if this is possible without building up the query yourself. The problem with this is that the way rails deals with eager loading isn't that good when you start getting at all conditional or trying to limit results. You can make includes nearly do what you want by creating another association:

    class Category < ActiveRecord::Base
      has_one :last_post, :class_name => "Post", :foreign_key => "category_id", :order => "created_at desc"
    end
    

    Then you can use includes normally:

    Category.includes(:last_post).all
    

    But, the problem with this is that because of the sql this generates, it's still selecting all posts from the categories even though when you call category.last_post it returns only the last post. It'll generate sql similar to this:

    SELECT `categories`.* FROM `categories`
    SELECT `posts`.* FROM `posts` WHERE (`posts`.category_id IN (1,2)) ORDER BY created_at desc