49

I have a model relationship where today has many tasks

I'm trying to retrieve a user's today object, include the tasks and render them all to Json. All of this was going great until I decided I wanted to order the tasks within the today object because respond_with block is also used for rendering the html page. Is there any way to include the tasks and order them?

I'm trying something like this:

class TodaysController < ApplicationController
  respond_to :html, :json
  def show
    @today = Today.where(:user_id => current_user.id).joins(:tasks).includes(:tasks).order(:priority).first
    respond_with @today, :include => :tasks
  end
end

This retrieves everything correctly, but does not seem to order the tasks at all.

This is what I used to have (which worked great, but didn't have the ordering):

class TodaysController < ApplicationController
  respond_to :html, :json
  def show
    @today = current_user.today
    respond_with @today, :include => :tasks
  end
end

I know I can retrieve the data and sort it afterwards like this:

@today = current_user.today
@today.tasks.sort!{|a,b| a.priority <=> b.priority }

This works and will pass my tests, but I was hoping for an ActiveRecord way to solve this.

sawa
  • 165,429
  • 45
  • 277
  • 381
royvandewater
  • 1,368
  • 2
  • 14
  • 16
  • Regarding your last line of code. The following is a bit clearer: `@today.tasks.sort_by(&:priority)`. – DNNX May 27 '13 at 11:45

2 Answers2

69

Try this in your Today model:

has_many :tasks, :order => 'priority DESC'

EDIT: As mentioned in comment below, in Rails 4+, this is now:

has_many :tasks, -> { order(:priority => :desc) }

(more info here)

Community
  • 1
  • 1
khelll
  • 23,590
  • 15
  • 91
  • 109
  • 6
    Is there no other way to do this? – maletor Jan 07 '13 at 21:58
  • 25
    In rails 4 this is now deprecated, the new approach would be `has_many :tasks, -> { order(:priority => :desc) }` [read about it here](http://stackoverflow.com/questions/18284606/deprecated-warning-for-rails-4-model-with-order) – Cluster Aug 19 '14 at 05:10
53

Direct solution would be to include the tasks table name before priority:

Today.where(:user_id => current_user.id).includes(:tasks).order('tasks.priority').first
# joins(:tasks) is not required

Or, if you don't want to have the table name hardcoded, you can merge with scope from Task model:

Today.where(:user_id => current_user.id).joins(:tasks).includes(:tasks).merge(Task.order(:priority)).first
# joins(:tasks) here is required

Also, you can add has_many: todays to User model to ditch the where clause and do:

current_user.todays.includes(:tasks).order('tasks.priority').first
# or
current_user.todays.joins(:tasks).includes(:tasks).merge(Task.order(:priority)).first

But if you need only/always to order by priority, and do not need other different orderings, adding order to has_many :tasks is easier.

Jack
  • 1,331
  • 1
  • 12
  • 9
  • 3
    I don't see why adding the order to the model is better. It seems to me that ordering is part of the view, and has nothing to do with either model. – B Seven Mar 24 '15 at 22:15
  • True. I meant easier. Edited. – Jack May 12 '15 at 08:49
  • @Jack `Today.where(:user_id => current_user.id).joins(:tasks).includes(:tasks).merge(Task.order(:priority)).first` its not working on rails 4.2 – Gagan Dec 07 '17 at 10:05
  • 2
    Should work, don't have 4.2 to test it now, for sure works in 5. Anyway, since 4.0 you should actually use `references` with `includes` to indicate you will use the table in the query. So it should be something like `Today.where(:user_id => current_user.id).includes(:tasks).references(:tasks).merge(Task.o‌​rder(:priority)).fir‌​st` – Jack Dec 14 '17 at 13:56