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)は、キャッシュミスを強制するためのものです(リレーションを強制的に再ロードするため)。ハッシュを指定すると、@ article.comments(true)と同じになります。.all(:order => '...')を使用することを忘れないでください。すでに数回私の足を骨折しました。
Marcel Jackwerth、2009

回答:


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によって大きく異なります。


ありがとう、「すべて」がおそらく最も簡単です。いい物!
ブライアンアームストロング

58
Rails 4では、注文オプションが削除されました。-> { 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関係については、引数の順序が重要です(2番目にする必要があります)。

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

あなたが常に同じ順序でアクセスのコメントになるでしょう場合は何もあなたも経由してこれを行うことができ、コンテキストに関係なくdefault_scopeComment等:

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を介して順序を付けるよりも大幅に遅くなります(そしてメモリを多く使用します)。

なんらかの理由でデフォルトの順序を追加するのが煩わしい場合や、特定の場合にデフォルトを上書きしたい場合は、フェッチアクション自体で指定するのは簡単です。

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

1
フェッチアクション自体のどこに指定できますか?モデルのメソッドをオーバーライドしますか?
ウィット

@Wit- .order()前の例のように、メソッドチェーンに追加できます。これはあなたが求めていることですか?
マットサンダーズ

ごめんなさい。何を成し遂げようとしていたのか思い出せない。
2007

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、次のように、ラムダの後に引数を追加する必要があります。

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.