Как автоматически отсортировать отношения has_many в Rails? - PullRequest
95 голосов
/ 11 апреля 2009

Это кажется очень простым вопросом, но я нигде не видел ответа.

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

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 }

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

Ответы [ 5 ]

146 голосов
/ 11 апреля 2009

Вы можете указать порядок сортировки для пустой коллекции с помощью опции 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')

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

38 голосов
/ 04 марта 2016

Начиная с 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

Однако это может быть проблематично по причинам, обсуждаемым в этом вопросе .

До 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
7 голосов
/ 11 апреля 2009

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

class Student < ActiveRecord::Base
  belongs_to :class

  default_scope :order => 'name'

end

Тогда, если вы позвоните

@students = @class.students

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

6 голосов
/ 11 апреля 2009

Вы можете использовать метод find ActiveRecord для получения ваших объектов и их сортировки.

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

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

0 голосов
/ 18 августа 2017

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

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...