Mengapa menggunakan rel default_scope sering kali tidak direkomendasikan?

127

Di mana-mana di dalam orang internet menyebutkan bahwa menggunakan rel default_scopeadalah ide yang buruk, dan top hits untuk default_scopedi stackoverflow adalah tentang bagaimana menimpanya. Ini terasa kacau, dan pantas untuk pertanyaan eksplisit (menurut saya).

Jadi: mengapa menggunakan rel default_scopedirekomendasikan?

wrtsprt
sumber

Jawaban:

192

Masalah 1

Mari pertimbangkan contoh dasarnya:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
end

Motivasi untuk menjadikan default published: true, mungkin untuk memastikan Anda harus menjelaskan ketika ingin menampilkan posting (pribadi) yang tidak dipublikasikan. Sejauh ini baik.

2.1.1 :001 > Post.all
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't'

Ini cukup banyak yang kami harapkan. Sekarang mari kita coba:

2.1.1 :004 > Post.new
 => #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>

Dan di sana kami memiliki masalah besar pertama dengan cakupan default:

=> default_scope akan mempengaruhi inisialisasi model Anda

Dalam contoh yang baru dibuat dari model seperti itu, default_scopeakan tercermin. Jadi meskipun Anda mungkin ingin memastikan untuk tidak membuat daftar posting yang tidak diterbitkan secara kebetulan, Anda sekarang membuat yang diterbitkan secara default.

Masalah 2

Pertimbangkan contoh yang lebih rumit:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
  belongs_to :user
end 

class User < ActiveRecord::Base
  has_many :posts
end

Mari kita dapatkan posting pengguna pertama:

2.1.1 :001 > User.first.posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't' AND "posts"."user_id" = ?  [["user_id", 1]]

Ini terlihat seperti yang diharapkan (pastikan untuk menggulir ke kanan untuk melihat bagian tentang user_id).

Sekarang kami ingin mendapatkan daftar semua posting - termasuk tidak dipublikasikan - katakanlah untuk tampilan pengguna yang masuk. Anda akan menyadari bahwa Anda harus 'menimpa' atau 'membatalkan' efek default_scope. Setelah google cepat, Anda mungkin akan mengetahui tentang unscoped. Lihat apa yang terjadi selanjutnya:

2.1.1 :002 > User.first.posts.unscoped
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"

=> Unscoped menghapus SEMUA cakupan yang mungkin biasanya berlaku untuk pilihan Anda, termasuk (tetapi tidak terbatas pada) asosiasi.

Ada beberapa cara untuk menimpa efek yang berbeda dari default_scope. Melakukan hal yang benar menjadi rumit dengan sangat cepat dan saya berpendapat bahwa tidak menggunakan default_scopesejak awal, akan menjadi pilihan yang lebih aman.

wrtsprt
sumber
2
Untuk menambahkan: satu-satunya saat saya menemukan default_scope berguna adalah ketika Anda benar-benar ingin memuat beberapa asosiasi secara default. default_scope {eager_load ([: category,: comments])}. Namun!!! Jika Anda melakukan kueri penghitungan pada model ini seperti Product.count, ini akan meningkatkan asosiasi untuk semua produk. Dan jika Anda memiliki 50K record, query count Anda hanya berubah dari 15ms menjadi 500ms, karena sementara yang Anda inginkan hanyalah menghitung, default_scope Anda akan tetap bergabung dengan yang lainnya.
konung
16
Bagi saya tampaknya masalahnya ada pada unscopedbukannya default_scopepada masalah # 2
Kapten Fogetti
4
@Captain Memang. Saya masih berpikir itu adalah ide yang baik untuk menyajikan kekurangan dari unscoped sebagai kemungkinan kerugian default_scope. Dalam kebanyakan kasus non-sepele, menggunakan default_scope akan membuat Anda perlu menggunakan tanpa cakupan. Ini adalah peringatan tingkat kedua (karena tidak ada istilah yang lebih baik), yang mudah terlewatkan saat meneliti metode.
wrtsprt
1
Masalah dengan kasus penggunaan dalam jawaban Anda adalah bahwa ada banyak kasus ketika Anda ingin menemukan postingan yang tidak dipublikasikan. Faktanya, saya berpendapat bahwa menemukan posting yang diterbitkan adalah kasus khusus. Satu-satunya saat Anda ingin posting yang diterbitkan adalah ketika seseorang melihat halaman publik. Namun ada kalanya Anda ingin melihat postingan yang tidak dipublikasikan.
B Tujuh
3
Saya kira baik penggunaan default_scopeadalah ketika Anda ingin sesuatu yang akan diurutkan: default_scope { order(:name) }.
pengguna2985898
9

Alasan lain untuk tidak menggunakan default_scopeadalah saat Anda menghapus instance model yang memiliki hubungan 1 ke banyak dengan default_scopemodel tersebut

Pertimbangkan misalnya:

    class User < ActiveRecord::Base
      has_many :posts, dependent: :destroy
    end 

    class Post < ActiveRecord::Base
      default_scope { where(published: true) }
      belongs_to :user
    end

Memanggil user.destroyakan menghapus semua postingan yang ada published, tetapi tidak akan menghapus postingan yang ada unpublished. Oleh karena itu database akan melempar pelanggaran kunci asing karena berisi record yang mereferensikan pengguna yang ingin Anda hapus.

Koekenbakker 28
sumber
6

default_scope sering kali direkomendasikan karena terkadang salah digunakan untuk membatasi set hasil. Penggunaan default_scope yang baik adalah untuk mengurutkan set hasil.

Saya akan menjauh dari menggunakan wheredi default_scope dan lebih suka membuat ruang lingkup untuk itu.

nahankid
sumber
1
Masalah kedua "Tidak tercakup menghapus SEMUA cakupan yang biasanya berlaku untuk pilihan Anda, termasuk (tetapi tidak terbatas pada) asosiasi" masih ada meskipun default_scopehanya berisi order. Perilaku unscopedini sangat tidak terduga.
Zack Xu
1

Bagi saya ini bukan sebuah ide yang buruk tetapi harus digunakan dengan hati-hati !. Ada kasus di mana saya selalu ingin menyembunyikan catatan tertentu ketika bidang ditetapkan.

  1. Lebih disukai default_scopeharus sesuai dengan nilai default DB (misalnya: { where(hidden_id: nil) })
  2. Ketika Anda benar-benar yakin ingin menampilkan rekaman tersebut, selalu ada unscopedmetode yang akan menghindari Andadefault_scope

Sehingga akan tergantung dan kebutuhan yang sebenarnya.

Sposmen
sumber
0

Saya hanya menemukan default_scopeberguna hanya dalam memesan beberapa parameter untuk masuk ascatau descdiurutkan dalam semua situasi. Kalau tidak, saya menghindarinya seperti wabah

Moses Liao GZ
sumber