如何在Rails中自动排序has_many关系?

时间:2009-04-10 21:42:48

标签: ruby-on-rails

这似乎是一个非常简单的问题,但我没有看到它在任何地方得到解答。

如果您有以下内容,请使用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 }

但有些东西告诉我它应该更简单。我错过了什么?

5 个答案:

答案 0 :(得分:146)

您可以使用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。

答案 1 :(得分:37)

从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 

正如Jim提到的,在获取结果之后你也可以使用sort_by,尽管在任何大小的结果集中,这将比通过SQL / ActiveRecord进行排序要慢得多(并且使用更多的内存)。

如果您正在做某些事情,由于某种原因添加默认订单很麻烦,或者您想在某些情况下覆盖默认值,那么在提取操作本身中指定它是很容易的:

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

答案 2 :(得分:7)

如果您使用的是Rails 2.3并希望对此对象的所有集合使用相同的默认顺序,则可以使用default_scope来订购您的集合。

class Student < ActiveRecord::Base
  belongs_to :class

  default_scope :order => 'name'

end

然后,如果你打电话

@students = @class.students

将根据您的default_scope订购它们。 TBH在一般意义上排序是对默认范围的唯一真正好用。

答案 3 :(得分:6)

您可以使用ActiveRecord的find方法获取对象并对其进行排序。

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

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

答案 4 :(得分:0)

如果你需要传递一些额外的参数,比如dependent: :destroy或其他什么,你应该在lambda之后追加一些,如下:

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end