ฉันจะเรียงลำดับความสัมพันธ์ has_many ใน Rails โดยอัตโนมัติได้อย่างไร


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 (จริง) อย่าลืมใช้. all (: order => '... ') หักขาของฉันสองสามครั้งแล้ว
Marcel Jackwerth

คำตอบ:


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 ตัวเลือกคำสั่งถูกลบออก ใช้แลมด้า-> { order(created_at: :desc) }แทน ดู: stackoverflow.com/questions/18284606/…
d_rail

สิ่งนี้เลิกใช้กับราง 4 ดูstackoverflow.com/questions/18284606/…
bjelli

42

ใน 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 

ดังที่จิมกล่าวไว้คุณสามารถใช้sort_byหลังจากที่คุณได้รับผลลัพธ์แม้ว่าในชุดผลลัพธ์ขนาดใดก็ตามสิ่งนี้จะช้าลงอย่างมาก (และใช้หน่วยความจำมากกว่า) มากกว่าการสั่งซื้อผ่าน SQL / ActiveRecord

หากคุณกำลังดำเนินการบางอย่างที่การเพิ่มคำสั่งเริ่มต้นเป็นเรื่องยุ่งยากด้วยเหตุผลบางประการหรือคุณต้องการลบล้างค่าเริ่มต้นของคุณในบางกรณีการระบุคำสั่งดังกล่าวในการดึงข้อมูลนั้นเป็นเรื่องสำคัญ:

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

1
ฉันจะระบุได้ที่ไหนในการดึงข้อมูลเอง ฉันจะแทนที่เมธอดในโมเดลหรือไม่
วิทย์

@Wit - คุณสามารถเพิ่มลง.order()ในห่วงโซ่วิธีการเช่นในตัวอย่างสุดท้าย นี่คือสิ่งที่คุณถาม?
Matt Sanders

ฉันขอโทษ. ฉันจำไม่ได้ว่าฉันพยายามบรรลุเป้าหมายอะไร
วิทย์

1
has_many ผ่านตัวอย่าง polymorphic มีประโยชน์มากที่นี่!
Vijay

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

6

คุณสามารถใช้วิธีค้นหาของ ActiveRecord เพื่อรับวัตถุของคุณและจัดเรียงได้เช่นกัน

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

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


3
หรือวิธีการชวเลข @ article.comments.all (: order => 'created_at DESC')
erik

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.