views:

1233

answers:

2

I have a structure something like this:

class User
  has_many :dongles
  has_many :licences, :through => :dongles
end

class Dongle
  has_many :licences
  belongs_to :user
end

class Licence
  belongs_to :dongle
end

However, time passes and the user eventually gets multiple licences for each dongle. Reasonably, the app wants to summarise the most recent licences for each.

I know I can naively do it like this:

user.dongles.each do |dongle|
  licence = dongle.licences.find(:first, :order => 'created_at DESC')
  # do something with the licence info
end

But is there a way to do this through the collection and avoid the large number of queries which would normally result by doing it the naive way?

I tried this:

user.licences.find(:all, :order => 'created_at DESC', :group => 'dongle_id')

This does return one licence for each dongle, but the first one it takes is decided by 'id', not by the order I have specified in the query.

Is there a way I can get it to give me the first of each, using a sort order I provide to decide which is the first?

+2  A: 

From your models, all association info has already been declared. You can actually use each of user to access dongles and licences info by performing a single query with ActiveRecord include options.

# Say the table name is licences and dongles.
users = User.find(:all, 
  :include => [:dongles, :licences], 
  :order => "licences.created_at DESC, dongles.created_at DESC")

I assume you want to create a summary of latest licence of each dongle owned by each user. You may cut the loop upon your actual needs.

users.each do |user| 
  # do something with your user info
  user.dongles.each do |dongle|
    # do something with your dongle info
    licence = dongle.licences.first
    # do something with the licence info
  end
end

Read more about this at http://snippets.dzone.com/posts/show/2089

manat
This is good as it does reduce the number of queries, while the code isn't much different from what I already had (which is more readable anyway.)
Trejkaz
A: 

Have you tried with a default scope? First, you can try to add the order in the has_many as I've shown in the User.

class User
  has_many :dongles
  has_many :licences, :through => :dongles, :order => 'created_at DESC'
end

However, I am not sure if that actually works with a has-many-through association, maybe, if that doesn't work you can try to add it to the association in Dongle instead.

class Dongle
  has_many :licences, :order => 'created_at DESC'
  belongs_to :user
end

The second option would be to try with a default scope as I've shown in the License.

class Licence
  default_scope :order => 'created_at DESC'
  belongs_to :dongle
end

after that it should be enough to just get it with user.licenses.find(:first)

Jimmy Stenke