martini-bonanza
martini-bonanza

Reputation: 1591

How to find the most recent associations created between two objects with Rails?

I have a user model, a movie model and this association in user.rb (I use has_many :through because there are other associations between these two models):

has_many :has_seens
has_many :movies_seen, :through => :has_seens, :source => :movie

I'm trying to get an array of the ten most recent has_seens associations created.

For now, the only solution I found is to crawl through all the users, creating an array with every user.has_seens found, then sort the array by has_seen.created_at and only keep the last 10 items… Seems like a heavy operation.

Is there a better way?

Kevin

Upvotes: 2

Views: 551

Answers (2)

John Topley
John Topley

Reputation: 115292

You should be able to accomplish this by adding an :order clause to the has_many :has_seens association to order them by the creation date. Then use an association extension to define a method on the association itself:

class User < ActiveRecord::Base
  has_many :has_seens, :order => 'created_at DESC' do
    def recent(count=10)
      all(:limit => count)
    end
  end
end

Now you can use user.has_seens.recent to get the most recent ten movies seen. Optionally, pass in a different count e.g. user.has_seens.recent(25).

Incidentally, I think that viewings is a less awkward association name than has_seens!


Edit: Personally, I would arrange things with a Viewing join model, like this:

class User < ActiveRecord::Base
  has_many :viewings
  has_many :movies, :through => :viewings
end

class Viewing < ActiveRecord::Base
  belongs_to :user
  belongs_to :movie

  default_scope :order => 'created_at DESC'

  # Recent n viewings for any user  
  named_scope :recent, lambda { |count| { :limit => count }} 
end

class Movie < ActiveRecord::Base
  has_many :viewings
  has_many :users, :through => :viewings
end

I've used a default scope on the Viewing model, but you could specify the ordering on the associations if viewings should be ordered by some other attribute other than creation date by default.

Now you can do:

# 10 most recent viewings for everyone
recent = Viewing.recent(10)

Upvotes: 1

Benjamin Manns
Benjamin Manns

Reputation: 9138

You can do that using find:

HasSeen.find(:order => 'created_at DESC', :limit => 10)

You can use a named_scope (inside HasSeen model) like this:

named_scope :recent, :order => 'created_at DESC', :limit => 10

and call HasSeen.recent.

If you are looking for the users, an easy (but not most efficient, I just don't know exactly how to do the join) way to do it is recent_users = HasSeen.recent.map { |h| h.user }

If the latter is what you are looking for, post a comment and I'll clean it up into a named_scope for the users model.

Upvotes: 0

Related Questions