2009-04-10 3 views
91

Это похоже на очень простой вопрос, но я нигде не видел ответа.Как автоматически сортировать отношения has_many в Rails?

В рельсах, если у вас есть:

class Article < ActiveRecord::Base 
    has_many :comments 
end 
class Comments < ActiveRecord::Base 
    belongs_to :article 
end 

Почему вы не можете заказать комментарии с чем-то вроде этого:

@article.comments(:order=>"created_at DESC") 

Именованная работает, если вам нужно ссылаться на него много и даже люди делают такие вещи:

@article.comments.sort { |x,y| x.created_at <=> y.created_at } 

Но что-то говорит мне, что это должно быть проще. Что мне не хватает?

+0

Будьте осторожны, вы используете неожиданный метод: @ article.comments (перезарядка = ложь) предназначен для принудительного кэширования (для принудительной перезагрузки re ляционной). Если вы предоставляете хэш, это то же самое, что и @ article.comments (true). Не забудьте использовать .all (: order => '...'). Несколько раз сломал ногу. –

ответ

143

Вы можете указать порядок сортировки голой коллекции с опционом на has_many себя:

class Article < ActiveRecord::Base 
    has_many :comments, :order => 'created_at DESC' 
end 
class Comment < ActiveRecord::Base 
    belongs_to :article 
end 

Или, если вы хотите простой, метод без базы данных сортировки , использование sort_by:

article.comments.sort_by &:created_at 

Соберите ИНГ это с ActiveRecord-добавляемыми методами упорядочения:

article.comments.find(:all, :order => 'created_at DESC') 
article.comments.all(:order => 'created_at DESC') 

Вашего пробег может изменяться: характеристики эффективности вышеуказанных решений изменятся дико в зависимости от того, как вы выборки данных, в первую очередь, и которые рубиновые вы» re для запуска вашего приложения.

+0

Спасибо, «все», вероятно, самый простой. Хорошая вещь! –

+48

в Rails 4, опция заказа была удалена. Вместо этого используйте lambda '-> {order (created_at:: desc)}'. См. Http://stackoverflow.com/questions/18284606/deprecated-warning-for-rails-4-model-with-order –

+0

Это устарело с помощью рельсов 4, см. Http://stackoverflow.com/questions/18284606/ устаревшие-предупреждающие для-рельсы-4-has-many-with-order – bjelli

7

Если вы используете Rails 2.3 и хотите использовать тот же порядок по умолчанию для всех коллекций этого объекта, вы можете использовать default_scope для заказа своей коллекции.

class Student < ActiveRecord::Base 
    belongs_to :class 

    default_scope :order => 'name' 

end 

Затем, если вы звоните

@students = @class.students 

Они будут упорядочены в соответствии с вашим default_scope. TBH в очень общем смысле - это действительно хорошее использование областей по умолчанию.

+0

С Rails 4 это не соответствует требованиям. См. Это решение для правильного синтаксиса Rails 4: http://stackoverflow.com/questions/18506038/rails-4-default-scope –

31

С Rails 4, вы могли бы сделать:

class Article < ActiveRecord::Base 
    has_many :comments, -> { order(created_at: :desc) } 
end 
class Comment < ActiveRecord::Base 
    belongs_to :article 
end 

Для has_many :through отношений вопросы, порядок аргументов (он должен быть вторым):

class Article 
    has_many :comments, -> { order('postables.sort' :desc) }, 
      :through => :postable 
end 

Если вы всегда хотите получить доступ в том же порядке, независимо от контекста, вы также можете сделать это через default_scope в пределах Comment:

class Comment < ActiveRecord::Base 
    belongs_to :article 
    default_scope { order(created_at: :desc) } 
end 

Однако это может быть проблематично для reasons discussed in this question.

Перед Rails 4 можно указать order в качестве ключа на отношения, как:

class Article < ActiveRecord::Base 
    has_many :comments, :order => 'created_at DESC' 
end 

Как Джим упомянул вы можете также использовать sort_by после того как вы неправдоподобные результаты, хотя в каких-либо результирующих наборов размера это будет значительно медленнее (и использовать намного больше памяти), чем выполнять заказы через SQL/ActiveRecord.

Если вы делаете что-то, где добавление заказа по умолчанию является громоздким по какой-то причине или вы хотите, чтобы переопределить значение по умолчанию в некоторых случаях, это тривиально, чтобы указать его в самом кокетливых действиях:

sorted = article.comments.order('created_at').all 
+0

Где я могу указать его в самом процессе выборки? Я переопределяю метод в модели? – Wit

+0

@Wit - вы можете добавить '.order()' в цепочку методов, как в последнем примере. Это то, что вы просите? –

+0

Прошу прощения. Я не могу вспомнить, чего я пытаюсь достичь. – Wit

0

И если вам нужно передать дополнительные аргументы, как dependent: :destroy или любой другой, вы должны добавить те, которые после того, как лямбда, как это:

class Article < ActiveRecord::Base 
    has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy 
end 
Смежные вопросы