Rails'te bir has_many ilişkisini otomatik olarak nasıl sıralayabilirim?


96

Bu gerçekten basit bir soru gibi görünüyor ama yanıtını hiçbir yerde görmedim.

Eğer varsa raylarda:

class Article < ActiveRecord::Base 
  has_many :comments 
end 
class Comments < ActiveRecord::Base 
  belongs_to :article 
end

Neden yorumları şöyle bir sipariş veremiyorsunuz:

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

Adlandırılmış kapsam, ona çok fazla başvurmanız gerekiyorsa ve hatta insanlar bunun gibi şeyler yaparsa işe yarar:

@article.comments.sort { |x,y| x.created_at <=> y.created_at }

Ama bir şey bana daha basit olması gerektiğini söylüyor. Neyi kaçırıyorum?


Dikkatli olun, beklenmedik bir yöntem kullanıyorsunuz: @ article.comments (reload = false) bir önbellek kaçırmaya zorlamaktır (bir ilişkiyi yeniden yüklemeye zorlamak için). Bir karma sağlarsanız, @ article.comments (doğru) ile aynıdır. .All (: order => '...') kullanmayı unutmayın. Bacağımı birkaç kez kırdım.
Marcel Jackwerth

Yanıtlar:


152

Çıplak koleksiyon için sıralama düzenini has_manykendi üzerinde bir seçenekle belirtebilirsiniz :

class Article < ActiveRecord::Base 
  has_many :comments, :order => 'created_at DESC'
end 
class Comment < ActiveRecord::Base 
  belongs_to :article 
end

Veya basit, veritabanı olmayan bir sıralama yöntemi istiyorsanız sort_by kullanın :

article.comments.sort_by &:created_at

Bunu ActiveRecord eklenmiş sıralama yöntemleriyle toplamak:

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

Kilometreniz değişiklik gösterebilir: Yukarıdaki çözümlerin performans özellikleri, ilk etapta verileri nasıl getirdiğinize ve uygulamanızı çalıştırmak için hangi Ruby'yi kullandığınıza bağlı olarak çılgınca değişecektir.


Teşekkürler, "hepsi" muhtemelen en basit olanıdır. İyi şeyler!
Brian Armstrong

58
Rails 4'te sipariş seçeneği kaldırılmıştır. -> { order(created_at: :desc) }Bunun yerine bir lambda kullanın. Bakınız: stackoverflow.com/questions/18284606/…
d_rail

bu, raylar 4 ile kullanımdan kaldırıldı, bkz. stackoverflow.com/questions/18284606/…
bjelli

42

Rails 4'ten itibaren şunları yapacaksınız:

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }
end 
class Comment < ActiveRecord::Base 
  belongs_to :article 
end

Bir has_many :throughilişki için argüman sırası önemlidir (ikinci olmalıdır):

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

Hep hayır bağlamını önemli aynı sırada erişim yorumlarla isteyeceksiniz Eğer siz de aracılığıyla bu yapabileceğini default_scopeiçinde Commentgibi:

class Comment < ActiveRecord::Base 
  belongs_to :article 
  default_scope { order(created_at: :desc) }
end

Ancak , bu soruda tartışılan nedenlerden dolayı bu sorunlu olabilir .

Rails 4'ten önce order, ilişkide bir anahtar olarak belirtebilirsiniz , örneğin:

class Article < ActiveRecord::Base 
  has_many :comments, :order => 'created_at DESC'
end 

Jim'in bahsettiği gibi, sort_bysonuçları getirdikten sonra da kullanabilirsiniz , ancak herhangi bir sonuç kümesinde bu, SQL / ActiveRecord aracılığıyla sipariş vermekten önemli ölçüde daha yavaş olacaktır (ve çok daha fazla bellek kullanacaktır).

Varsayılan sipariş eklemenin herhangi bir nedenle zahmetli olduğu bir şey yapıyorsanız veya belirli durumlarda varsayılanınızı geçersiz kılmak istiyorsanız, bunu getirme eyleminin kendisinde belirtmek önemsizdir:

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

1
Getirme eyleminin kendisinde bunu nerede belirtebilirim? Modeldeki bir yöntemi geçersiz kılıyor muyum?
Wit

@Wit - .order()son örnekte olduğu gibi yöntem zincirine ekleyebilirsiniz . Sorduğun bu mu?
Matt Sanders

Üzgünüm. Ne başarmaya çalıştığımı hatırlayamıyorum.
Wit

1
Has_many, polimorfik örnek aracılığıyla burada çok yararlıdır!
Vijay

7

Rails 2.3 kullanıyorsanız ve bu nesnenin tüm koleksiyonları için aynı varsayılan sıralamayı kullanmak istiyorsanız, koleksiyonunuzu sipariş etmek için default_scope kullanabilirsiniz.

class Student < ActiveRecord::Base
  belongs_to :class

  default_scope :order => 'name'

end

Sonra ararsan

@students = @class.students

Default_scope'unuza göre sıralanacaklar. TBH, çok genel bir anlamda, varsayılan kapsamların gerçekten iyi olan tek kullanımıdır.


Rails 4 itibariyle bu uyumlu değildir. Doğru Rails 4 sözdizimi için bu çözüme bakın: stackoverflow.com/questions/18506038/rails-4-default-scope
Kees Briggs


0

Ve buna benzer ek argümanlar dependent: :destroyiletmeniz gerekirse, lambdadan sonra olanları şu şekilde eklemelisiniz:

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.