Làm cách nào để tự động sắp xếp mối quan hệ has_many trong Rails?


96

Đây có vẻ như là một câu hỏi thực sự đơn giản nhưng tôi chưa thấy nó trả lời ở đâu.

Trong đường ray nếu bạn có:

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

Tại sao bạn không thể sắp xếp các bình luận với những thứ như thế này:

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

Phạm vi được đặt tên hoạt động nếu bạn cần tham khảo nhiều và thậm chí mọi người làm những việc như thế này:

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

Nhưng điều gì đó nói với tôi rằng nó nên đơn giản hơn. Tôi đang thiếu gì?


Hãy cẩn thận, bạn đang sử dụng một phương pháp không mong muốn: @ article.comments (reload = false) là để buộc bỏ sót bộ nhớ cache (để buộc tải lại một quan hệ). Nếu bạn cung cấp một hàm băm, nó giống như @ article.comments (true). Đừng quên sử dụng .all (: order => '...'). Gãy chân tôi vài lần rồi.
Marcel Jackwerth

Câu trả lời:


152

Bạn có thể chỉ định thứ tự sắp xếp cho tập hợp trống với một tùy chọn trên has_manychính nó:

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

Hoặc, nếu bạn muốn một phương pháp sắp xếp đơn giản, không phải cơ sở dữ liệu, hãy sử dụng sort_by :

article.comments.sort_by &:created_at

Thu thập thông tin này bằng các phương pháp đặt hàng được thêm vào ActiveRecord:

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

Số dặm của bạn có thể thay đổi: đặc điểm hiệu suất của các giải pháp trên sẽ thay đổi nhiều tùy thuộc vào cách bạn tìm nạp dữ liệu ngay từ đầu và Ruby nào bạn đang sử dụng để chạy ứng dụng của mình.


Cảm ơn, "tất cả" có lẽ là đơn giản nhất. Đồ tốt!
Brian Armstrong,

58
trong Rails 4, tùy chọn thứ tự đã bị xóa. Sử dụng lambda -> { order(created_at: :desc) }để thay thế. Xem: stackoverflow.com/questions/18284606/...
d_rail

tính năng này không được dùng nữa với rails 4, xem stackoverflow.com/questions/18284606/…
bjelli

41

Đối với Rails 4, bạn sẽ làm:

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

Đối với một has_many :throughmối quan hệ, thứ tự đối số quan trọng (nó phải là thứ hai):

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

Nếu bạn luôn muốn bình luận truy cập theo thứ tự không quan trọng bối cảnh mà bạn cũng có thể làm được điều này thông qua default_scopetrong Commentnhư:

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

Tuy nhiên, điều này có thể có vấn đề vì những lý do được thảo luận trong câu hỏi này .

Trước Rails 4, bạn có thể chỉ định orderlàm khóa cho mối quan hệ, như:

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

Như Jim đã đề cập, bạn cũng có thể sử dụng sort_bysau khi bạn đã tìm nạp kết quả mặc dù trong bất kỳ tập hợp kích thước kết quả nào, điều này sẽ chậm hơn đáng kể (và sử dụng nhiều bộ nhớ hơn) so với việc đặt hàng của bạn thông qua SQL / ActiveRecord.

Nếu bạn đang làm điều gì đó mà việc thêm đơn hàng mặc định là cồng kềnh vì lý do nào đó hoặc bạn muốn ghi đè thứ tự mặc định của mình trong một số trường hợp nhất định, thì việc chỉ định nó trong chính hành động tìm nạp là rất nhỏ:

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

1
Tôi có thể chỉ định nó ở đâu trong chính hành động tìm nạp? Tôi có ghi đè một phương thức trong mô hình không?
Wit

@Wit - bạn có thể thêm .order()vào chuỗi phương thức, như trong ví dụ cuối cùng. Đây có phải là những gì bạn đang hỏi?
Matt Sanders

Tôi xin lỗi. Tôi không thể nhớ những gì tôi đã cố gắng đạt được.
Chứng kiến

Has_many, thông qua ví dụ đa hình là cực kỳ hữu ích ở đây!
Vijay

7

Nếu bạn đang sử dụng Rails 2.3 và muốn sử dụng cùng một thứ tự mặc định cho tất cả các bộ sưu tập của đối tượng này, bạn có thể sử dụng default_scope để sắp xếp bộ sưu tập của mình.

class Student < ActiveRecord::Base
  belongs_to :class

  default_scope :order => 'name'

end

Sau đó, nếu bạn gọi

@students = @class.students

Chúng sẽ được sắp xếp theo default_scope của bạn. TBH theo một nghĩa chung chung là thứ tự là cách sử dụng thực sự tốt duy nhất của phạm vi mặc định.


Đối với Rails 4, điều này không tuân thủ. Xem giải pháp này để biết cú pháp Rails 4 chính xác: stackoverflow.com/questions/18506038/rails-4-default-scope
Kees Briggs


0

Và nếu bạn cần chuyển một số đối số bổ sung như dependent: :destroyhoặc bất cứ điều gì, bạn nên nối các đối số sau một lambda, như thế này:

class Article < ActiveRecord::Base 
  has_many :comments, -> { order(created_at: :desc) }, dependent: :destroy
end
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.