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


96

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

在滑轨中,如果您有:

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 }

但是有件事告诉我它应该更简单。我想念什么?


请注意,您使用的是意外的方法:@ article.comments(reload = false)会强制执行cache-miss(以强制重新加载关系)。如果提供哈希,则与@ article.comments(true)相同。不要忘记使用.all(:order =>'...')。我的腿已经断了几次。
Marcel Jackwerth,09年

Answers:


152

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


谢谢,“全部”可能是最简单的。好东西!
Brian Armstrong,

58
在Rails 4中,订单选项已被删除。请改用lambda -> { order(created_at: :desc) }。请参阅:stackoverflow.com/questions/18284606/...
d_rail

它已被轨道4弃用,请参见stackoverflow.com/questions/18284606/…–
bjelli

40

从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 

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

如果您出于某种原因在添加默认订单的过程中很麻烦,或者在某些情况下想要覆盖默认订单,则在fetching操作本身中指定它很简单:

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

1
在提取操作本身中可以在哪里指定?我是否要覆盖模型中的方法?
机智

@Wit-您可以添加.order()到方法链中,就像上一个示例一样。这是您要问的吗?
马特·桑德斯

对不起。我不记得我想要达到的目标。
机智

7

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

class Student < ActiveRecord::Base
  belongs_to :class

  default_scope :order => 'name'

end

那你打电话

@students = @class.students

它们将根据您的default_scope进行排序。从一般意义上讲,TBH是默认范围的唯一真正好的用法。


从Rails 4开始,这是不兼容的。有关正确的Rails 4语法,请参见以下解决方案:stackoverflow.com/questions/18506038/rails-4-default-scope
Kees Briggs


0

并且,如果您需要传递其他类似的参数dependent: :destroy,则应在lambda之后附加这些参数,如下所示:

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.