Urutan sortir default untuk model rel?

255

Saya ingin menentukan urutan penyortiran default dalam model saya.

Sehingga ketika saya melakukan .where()tanpa menentukan .order()itu menggunakan semacam default. Tetapi jika saya menentukan .order(), itu menimpa default.

Justin Tanner
sumber

Jawaban:

544

default_scope

Ini berfungsi untuk Rails 4+:

class Book < ActiveRecord::Base
  default_scope { order(created_at: :desc) }
end

Untuk Rails 2.3, 3, Anda membutuhkan ini:

default_scope order('created_at DESC')

Untuk Rails 2.x:

default_scope :order => 'created_at DESC'

Di mana created_atbidang yang Anda inginkan penyortiran default dilakukan.

Catatan: ASC adalah kode yang digunakan untuk Ascending dan DESC adalah untuk descending ( desc, NOT dsc !).

scope

Setelah terbiasa, Anda juga dapat menggunakan scope:

class Book < ActiveRecord::Base
  scope :confirmed, :conditions => { :confirmed => true }
  scope :published, :conditions => { :published => true }
end

Untuk Rails 2 Anda perlu named_scope.

:publishedcakupan memberi Anda Book.publishedalih-alih Book.find(:published => true).

Karena Rails 3 Anda dapat 'rantai' metode-metode tersebut bersama-sama dengan menggabungkannya dengan periode di antara mereka, jadi dengan cakupan di atas sekarang Anda dapat menggunakan Book.published.confirmed.

Dengan metode ini, permintaan tidak benar-benar dieksekusi sampai hasil aktual diperlukan (evaluasi malas), sehingga 7 cakupan dapat dirantai bersama tetapi hanya menghasilkan 1 permintaan basis data aktual, untuk menghindari masalah kinerja dari mengeksekusi 7 permintaan terpisah.

Anda dapat menggunakan parameter yang diteruskan seperti tanggal atau user_id (sesuatu yang akan berubah pada saat run-time dan akan membutuhkan 'evaluasi malas', dengan lambda, seperti ini:

scope :recent_books, lambda 
  { |since_when| where("created_at >= ?", since_when) }
  # Note the `where` is making use of AREL syntax added in Rails 3.

Akhirnya Anda dapat menonaktifkan ruang lingkup default dengan:

Book.with_exclusive_scope { find(:all) } 

atau bahkan lebih baik:

Book.unscoped.all

yang akan menonaktifkan filter apa pun (kondisi) atau urutkan (dipesan berdasarkan).

Perhatikan bahwa versi pertama berfungsi di Rails2 + sedangkan yang kedua (tidak dicopot) hanya untuk Rails3 +


Jadi ... jika Anda berpikir, hmm, jadi ini seperti metode maka ..., ya, itulah tepatnya lingkup ini!
Mereka seperti memiliki def self.method_name ...code... endtetapi seperti biasa dengan ruby ​​mereka adalah cara pintas sintaksis kecil yang bagus (atau 'gula') untuk membuat segalanya lebih mudah bagi Anda!

Sebenarnya mereka adalah metode tingkat Kelas karena mereka beroperasi pada 1 set 'semua' catatan.

Namun format mereka berubah, dengan rails 4 ada peringatan penghentian ketika menggunakan #scope tanpa melewati objek yang bisa dipanggil. Misalnya cakupan: merah, di mana (warna: 'merah') harus diubah scope :red, -> { where(color: 'red') }.

Sebagai catatan, ketika digunakan secara tidak benar, standar _scope dapat disalahgunakan / disalahgunakan.
Ini terutama tentang kapan akan digunakan untuk tindakan seperti wheremembatasi (memfilter) pilihan default ( ide buruk untuk default) daripada hanya digunakan untuk memesan hasil.
Untuk wherepilihan, cukup gunakan cakupan bernama biasa. dan menambahkan lingkup itu di dalam kueri, misalnya di Book.all.publishedmana publishedruang lingkup bernama.

Kesimpulannya, cakupan benar-benar hebat dan membantu Anda untuk mendorong berbagai hal ke dalam model untuk pendekatan DRYer 'pengendali model kurus'.

Michael Durrant
sumber
1
Harap pertimbangkan peringatan Dave Thomas tentang menggunakan default_scope sebelum menggunakannya sebagaimana dijelaskan dalam posting ini: pragdave.blogs.pragprog.com/pragdave/2012/03/…
reto
3
bukankah itu lebih aman untuk dilakukan default_scope { order("#{table_name}.created_at DESC") }?
cyrilchampier
37
Rails 4:default_scope { order(created_at: :desc) }
Marcus
2
Setidaknya 4.2.6sepertinya urutkan berdasarkan updated_attidak created_at.
Ain Tohvri
2
@AinTohvri benar. Ini mengejutkan saya dalam Rails 4.2. Mengapa disortir updated_atsecara default? : - |
sixty4bit
112

Pembaruan cepat untuk jawaban luar biasa Michael di atas.

Untuk Rails 4.0+, Anda harus meletakkan sortir di blok seperti ini:

class Book < ActiveRecord::Base
  default_scope { order('created_at DESC') }
end

Perhatikan bahwa pernyataan pesanan ditempatkan di blok yang dilambangkan oleh kurung kurawal.

Mereka mengubahnya karena terlalu mudah untuk melewatkan sesuatu yang dinamis (seperti waktu sekarang). Ini menghilangkan masalah karena blok dievaluasi saat runtime. Jika Anda tidak menggunakan blok Anda akan mendapatkan kesalahan ini:

Dukungan untuk memanggil #default_scope tanpa blok dihapus. Sebagai contoh alih-alih default_scope where(color: 'red'), silakan gunakan default_scope { where(color: 'red') }. (Atau Anda dapat mendefinisikan ulang self.default_scope.)

Seperti @Dan menyebutkan dalam komentarnya di bawah ini, Anda dapat melakukan sintaksis lebih ruby ​​seperti ini:

class Book < ActiveRecord::Base
  default_scope { order(created_at: :desc) }
end

atau dengan banyak kolom:

class Book < ActiveRecord::Base
  default_scope { order({begin_date: :desc}, :name) }
end

Terima kasih @Dan !

Paul Oliver
sumber
28
Di rel 4 ini juga dapat ditulis seolah- default_scope { order(created_at: :desc) }olah, seperti saya, Anda mencoba meminimalkan sintaks sql di rails. <br/> Jika Anda memiliki beberapa kolom untuk memesan oleh dan Anda ingin menggunakan sintaks baru Anda mungkin perlu membungkus desc kolom dalam kumis seperti inidefault_scope { order({begin_date: :desc}, :name) }
Dan
1
@Dan - Tidak hanya komentar Anda menghilangkan SQL, itu adalah sintaksis yang lebih Ruby.
B Seven