This was a sneaky bug that I managed to track down in my code… I hope
it helps someone. I first thought this might be a shortcoming or a bug
in Rails but I’m not really sure where the fault lies: I guess it’s
just an unexpected or suprising behaviour, or else my misuse of
ActiveRecord’s magic.
Problem simplified down to the ubiquitous Project/Task paradigm.
Let’s use acts_as_list for convenience.
class Project < ActiveRecord::Base
has_many :tasks, :order => :position
def next_action
tasks.first
end
end
class Task < ActiveRecord::Base
belongs_to :project
acts_as_list :scope => :project_id
end
Let’s create a project with some tasks and move
the last one to the top, as might happen in normal use:
p = Project.create :name => “Halloween costume”
task_names = [“paint wings”, “recharge lightsaber”, “get bear slippers”]
task_names.each do |t|
p.tasks << Task.new :name => t
end
p.tasks.last.move_to_top
Now in our controller, we want to select all projects ordered by name.
We’ll also want to display the next action… so let’s
eager load the tasks association:
@projects = Project.find :all, :order => :name, :include => :tasks
in the view:
<% @projects.each do |project| %>
<%= project.next_action %>
<% end %>
Surprise! the next action is not the one we moved to the top! What I’m
understanding is that the find action uses only :order => :name, and
replaces the position ordering which is specified in the model.
Thoughts:
Could Rails handle this more intelligently for us? I had expected that
it would pile on the :order statements like “ORDER BY project.name,
task.position” but it doesn’t seem to do that at all.
Or else, should rails put up a warning when something like this occurs
(when an existing ordering is overwritten)?
To work around this, Instead of the method above, I added a proxy on
the Project, replacing the next_action method above:
has_one :next_action, :class_name => ‘Task’, :conditions => {
:position => 1 }
This seems to work, and I can now include just the next_action
association where needed. Problem solved. But, this works because we’re
no longer using a has_many association at all. I’m still wondering if
it’s possible to maintain multiple ordering with has_many… Any
thoughts on this?
–Andrew V.