Как автоматически сортировать отношения 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 }
Но что-то говорит мне, что это должно быть проще. Что мне не хватает?
Ответы
Ответ 1
Вы можете указать порядок сортировки для голого набора с опцией самой 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 вы используете для запуска своего приложения.
Ответ 2
Как и в Rails 4, вы должны:
class Article < ActiveRecord::Base
has_many :comments, -> { order(created_at: :desc) }
end
class Comment < ActiveRecord::Base
belongs_to :article
end
При a 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
Ответ 3
Если вы используете Rails 2.3 и хотите использовать тот же порядок по умолчанию для всех коллекций этого объекта, вы можете использовать default_scope для заказа своей коллекции.
class Student < ActiveRecord::Base
belongs_to :class
default_scope :order => 'name'
end
Затем, если вы вызываете
@students = @class.students
Они будут заказываться в соответствии с вашим значением default_scope. TBH в очень общем порядке упорядочения является единственным действительно хорошим использованием областей по умолчанию.
Ответ 4
Вы можете использовать метод поиска ActiveRecord для получения ваших объектов и сортировки их тоже.
@article.comments.find(:all, :order => "created_at DESC")
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Ответ 5
И если вам нужно передать некоторые дополнительные аргументы, например dependent: :destroy
или что-то еще, вы должны добавить их после лямбда, например:
class Article < ActiveRecord::Base
has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end