Mengganti Rails default_scope

156

Jika saya memiliki model ActiveRecord :: Base dengan lingkup standar:

class Foo < ActiveRecord::Base

  default_scope :conditions => ["bar = ?",bar]

end

Apakah ada cara untuk melakukan Foo.find tanpa menggunakan default_scopeketentuan? Dengan kata lain, dapatkah Anda mengganti ruang lingkup default?

Saya akan berpikir bahwa menggunakan 'default' dalam nama akan menyarankan bahwa itu bisa ditimpa, kalau tidak akan disebut sesuatu seperti global_scope, kan?

Gareth
sumber

Jawaban:

151

Jawaban singkat: Jangan gunakan default_scopekecuali Anda benar-benar harus. Anda mungkin akan lebih baik dengan lingkup bernama. Dengan itu, Anda dapat menggunakan with_exclusive_scopeuntuk mengganti ruang lingkup default jika perlu.

Lihat pertanyaan ini untuk lebih jelasnya.

Pär Wieslander
sumber
Terima kasih atas tautan ke pertanyaan sebelumnya
Gareth
10
> Jangan gunakan default_scope kecuali Anda benar-benar harus. Saran yang sangat bagus! Terima kasih!
installero
2
Benar sekali. Menggunakan default_scopemungkin tampak seperti ide yang bagus, tetapi kemungkinan akan menyebabkan beberapa sakit kepala selama masa aplikasi Anda.
thomax
1
Anda melebih-lebihkan sedikit Pak. default_scopeadalah alat yang sangat baik dan ada situasi di mana Anda bisa dengan cara lain tetapi default_scopeitu adalah hal yang tepat untuk dilakukan. Misalnya, ketika Anda memiliki Productmodel yang memiliki inactivebendera, atur adefault_scope { where inactive: false } adalah hal terbaik untuk dilakukan, karena 99% atau case Anda tidak ingin menampilkan produk yang tidak aktif. Kemudian Anda tinggal memanggil unscoped1% kasing yang tersisa, yang mungkin merupakan panel Admin.
pedrozath
215

Dalam Rails 3:

foos = Foo.unscoped.where(:baz => baz)
Vincent
sumber
58
Ini memiliki efek samping, jika Posting has_many Komentar, Post.first.comments.unscoped mengembalikan SEMUA komentar.
Enrico Carlesso
3
Ini benar-benar mengacaukan saya untuk sementara waktu. Terutama jika Anda akhirnya meletakkan ini dalam metode kelas seperti: def self.random; unscoped.order('rand()'); enddihapus menghapus SEMUA sql sebelum itu, bukan hanya apa yang terdaftar di bawah default_scope. Sementara secara teknis jawaban yang benar, berhati-hatilah menggunakanunstopped
Schneems
7
PERINGATAN! Unscoped TIDAK hanya menghapus default_scope, itu sudah dikatakan di komentar lain tetapi benar-benar dapat mengacaukan hal-hal.
dsimard
15
Aturan praktis yang baik adalah untuk hanya unscopedketika dapat langsung mengikuti model, misalnya Foo.unscoped.blah()ok tapi tidak pernah Foo.blah().unscoped.
Grant Birchmeier
stackoverflow.com/questions/1834159/... bekerja di sekitar efek samping yang disebutkan oleh Enrico
wbharding
107

Jika Anda hanya perlu mengubah urutan yang ditentukan default_scope, Anda dapat menggunakan reordermetode ini .

class Foo < ActiveRecord::Base
  default_scope order('created_at desc')
end

Foo.reorder('created_at asc')

menjalankan SQL berikut:

SELECT * FROM "foos" ORDER BY created_at asc
Gugurkan
sumber
3
Kiat: tentukan lingkup seperti scope :without_default_order, -> { reorder("") }dan Anda dapat melakukan hal-hal seperti Foo.without_default_order.order("created_at ASC")Dalam beberapa situasi ini berbunyi lebih baik (mungkin bukan situasi yang tepat ini, tapi saya punya satu).
Henrik N
Menyusun ulang melakukannya untuk saya. Terima kasih banyak!
Andre Zimpel
49

Karena 4.1Anda dapat menggunakan ActiveRecord::QueryMethods#unscopeuntuk melawan ruang lingkup default:

class User < ActiveRecord::Base
  default_scope { where tester: false }
  scope :testers, -> { unscope(:where).where tester: true }
  scope :with_testers, -> { unscope(:where).where tester: [true, false] }
  # ...
end

Hal ini saat ini mungkin untuk unscopehal-hal seperti: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Tapi tetap tolong hindari penggunaan default_scopejika Anda bisa . Ini untuk kebaikanmu sendiri.

jibiel
sumber
Jawaban ini harus lebih tinggi
Stephen Corwin
5

Rails 3 default_scope tampaknya tidak ditimpa seperti di Rails 2.

misalnya

class Foo < ActiveRecord::Base
  belongs_to :bar
  default_scope :order=>"created_at desc"
end

class Bar < ActiveRecord::Base
  has_many :foos
end

> Bar.foos
  SELECT * from Foo where bar_id = 2 order by "created_at desc";
> Bar.unscoped.foos
  SELECT * from Foo;  (WRONG!  removes the "has" relationship)
> Bar.foos( :order=>"created_at asc" )  # trying to override ordering
  SELECT * from Foo where bar_id = 2 order by "created_at desc, created_at asc"

Di aplikasi saya, menggunakan PostgreSQL, pemesanan dalam lingkup default MENANG. Saya menghapus semua default_scope saya dan mengkodekannya secara eksplisit di mana-mana.

Pitfall Rails3!

vanboom
sumber
1
Anda harus menggunakanBar.foos.reorder(:created_at => :asc)
Ivan Stana
5

Dengan Rails 3+ Anda dapat menggunakan kombinasi unscoped dan menggabungkan:

# model User has a default scope
query = User.where(email: "[email protected]")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)
santervo
sumber
Ini juga bekerja untuk saya, untuk memanggil unscoped terlebih dahulu (Rails 4.2):User.unscoped.where(email: "[email protected]")
Nick
4

Pada Rails 5.1+ (dan mungkin lebih awal, tapi saya telah mengujinya berfungsi pada 5.1) dimungkinkan untuk membuka penutup kolom tertentu, yang imho adalah solusi ideal untuk menghapus default_scopedengan cara yang dapat digunakan di dalam lingkup bernama. Dalam kasus OP default_scope,

Foo.unscope(where: :bar)

Atau

scope :not_default, -> { unscope(where: :bar) }
Foo.not_default

Keduanya akan menghasilkan kueri sql yang tidak menerapkan ruang lingkup asli, tetapi tidak menerapkan kondisi apa pun lainnya yang digabungkan ke dalam arel.

wbharding
sumber
2

Ya, Anda selalu dapat menggunakan favorit waktu lama find_by_sqldengan kueri lengkap. Sebagai contoh: Model.find_by_sql ("SELECT * FROM model WHERE id = 123")

Ady Rosen
sumber