0

I have a class like this:

class Project < ActiveRecord::Base
  has_many :versions, :order => :position
end

In a controller action, I need to query for a project's versions. I have the project ID. I could simply do a

versions = Project.find(params[:project_id]).versions

However, this will first do a query to the "projects" table, which is unnecessary in my case. I could change it to

versions = Version.find( :conditions => { :project_id => params[:project_id } )

(or use a dynamic finder with the same results)

This doesn't use the additional attributes of the association (in this case, the ":order => :position"), so if I wanted to get exactly the same result as in the same example, I will have to write:

versions = Version.find( :conditions => { :project_id => params[:project_id },
                         :order => :position )

And here I'm no longer DRY since I'm repeating something (the ":order" option) that is already defined in the model.

Is there a way to use and follow the association defined in the model without having to load the parent object? I'm currently still using Rails 2.3.8.

Muntasim
  • 6,689
  • 3
  • 46
  • 69
sys64738
  • 41
  • 6

2 Answers2

0

Sorry but I'm pretty sure you cannot do that - at least not following the public ActiveRecord API.

You could write extension that uses internal ActiveRecord API to inspect association options, but I wouldn't recommend this.

You could reuse some conditions through scopes but since you're still on Rails 2.3 that is not helping you.

I think your best bet is manually fetching version instances as you have written in the last example:

versions = Version.find( :conditions => { :project_id => params[:project_id },
                         :order => :position )

EDIT: What you could do in Rails4:

You can reuse and share scopes between models and associations like this:

class Project < ActiveRecord::Base
  has_many :versions, -> { ordered }
end

class Version
  def self.ordered
    order(:position)
  end
end

versions = Version.find_by(:project_id, params[:project_id]).ordered

The more scopes and the more complicated they are, the more useful this approach is.

jurglic
  • 3,599
  • 1
  • 17
  • 13
0

I would recommend to use one default_scope for ordering and another scope to find versions by project in Version model.

Project

class Project < ActiveRecord::Base
  has_many :versions
end

Version

 class Version < ActiveRecord::Base
   belongs_to :project
   named_scope :order_by_position, {:order => "position"} 
   named_scope :by_project_id, (lambda do |project_id| 
          {:conditions => ['project_id = ?', project_id]}
   end)
 end

Then you can use:

versions  = Version.by_project_id(params[:project_id])
Muntasim
  • 6,689
  • 3
  • 46
  • 69
  • Thank you, this works. However it makes the ordering the default to all queries on the Version model, which may not be desired (e.g. if Version is used by multiple associations with different ordering requirements). It may also be viewed as a disadvantage that the ordering is no longer visible by just looking at the Project model. – sys64738 Aug 25 '13 at 12:29
  • Yes, default scope is not recommended, I've edited the answer to its max fit – Muntasim Aug 25 '13 at 12:50
  • Well, now you still have to explicitly call order_by_position(). It is like in my original find() call, just different syntax. I was wondering how I could reuse the :order clause from the model/association without having to repeat it in soem way in the code. – sys64738 Aug 25 '13 at 13:13