Ghi đè Rails default_scope


155

Nếu tôi có mô hình ActiveRecord :: Base với phạm vi mặc định:

class Foo < ActiveRecord::Base

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

end

Có cách nào để làm Foo.find mà không sử dụng các default_scopeđiều kiện? Nói cách khác, bạn có thể ghi đè một phạm vi mặc định?

Tôi đã có thể nghĩ rằng việc sử dụng 'mặc định' trong tên sẽ đề nghị rằng nó Overridable, nếu không nó sẽ được gọi là một cái gì đó giống như global_scope, phải không?


Câu trả lời:


151

Câu trả lời ngắn: Không sử dụng default_scopetrừ khi bạn thực sự phải làm. Có lẽ bạn sẽ tốt hơn với phạm vi được đặt tên. Như đã nói, bạn có thể sử dụng with_exclusive_scopeđể ghi đè phạm vi mặc định nếu bạn cần.

Hãy xem câu hỏi này để biết thêm chi tiết.


Cảm ơn liên kết đến câu hỏi trước
Gareth

10
> Đừng sử dụng default_scope trừ khi bạn thực sự phải làm vậy. Một lời khuyên tuyệt vời! Cảm ơn bạn!
installero

2
Thật vậy. Sử dụng default_scopecó vẻ như là một ý tưởng tốt, nhưng có thể sẽ gây ra nhiều vấn đề đau đầu trong suốt thời gian sử dụng ứng dụng của bạn.
thomax


1
Bạn đang phóng đại một chút thưa ông. default_scopelà một công cụ tuyệt vời và có những tình huống mà bạn có thể theo một cách khác nhưng đó default_scopelà điều đúng đắn để làm. Ví dụ: khi bạn có một Productmô hình có inactivecờ, cài đặt default_scope { where inactive: false }là điều tốt nhất để làm, vì 99% hoặc các trường hợp bạn sẽ không muốn hiển thị một sản phẩm không hoạt động. Sau đó, bạn chỉ cần gọi unscopedcho các trường hợp 1% còn lại, có thể là bảng Quản trị.
pedrozath

215

Trong Rails 3:

foos = Foo.unscoped.where(:baz => baz)

58
Điều này có tác dụng phụ, nếu Post has_many Comment, Post.first.comments.unscoped trả về TẤT CẢ các bình luận.
Enrico Carlesso

3
Điều này thực sự làm tôi khó chịu trong một thời gian. Đặc biệt là nếu bạn kết thúc việc này trong một phương thức lớp như: def self.random; unscoped.order('rand()'); endunscoped sẽ xóa TẤT CẢ sql trước nó, không chỉ những gì được liệt kê trong default_scope. Về mặt kỹ thuật là một câu trả lời đúng, hãy cẩn thận khi sử dụngunstopped
Schneems

7
CẢNH BÁO! Unscoped KHÔNG chỉ xóa default_scope, nó đã được nói trong một bình luận khác nhưng nó thực sự có thể gây rối với mọi thứ.
fax

15
Một nguyên tắc tốt là chỉ unscopedkhi nó có thể theo mô hình trực tiếp, ví dụ như Foo.unscoped.blah()ok nhưng không bao giờ Foo.blah().unscoped.
Grant Birchmeier

stackoverflow.com/questions/1834159/ đá hoạt động xung quanh hiệu ứng phụ được đề cập bởi Enrico
wbhending

107

Nếu tất cả những gì bạn cần là thay đổi thứ tự được xác định trong default_scope, bạn có thể sử dụng reorderphương thức .

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

Foo.reorder('created_at asc')

chạy SQL sau:

SELECT * FROM "foos" ORDER BY created_at asc

3
Mẹo: xác định phạm vi như scope :without_default_order, -> { reorder("") }và bạn có thể thực hiện những việc như Foo.without_default_order.order("created_at ASC")Trong một số tình huống, nó đọc tốt hơn (có thể không phải là tình huống chính xác này, nhưng tôi đã có một).
Henrik N

Sắp xếp lại đã làm điều đó cho tôi. Cảm ơn rất nhiều!
Andre Zimpel

49

4.1bạn có thể sử dụng ActiveRecord::QueryMethods#unscopeđể chống lại phạm vi mặc định:

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

Đó là hiện thể unscopenhững thứ như: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Nhưng vẫn vui lòng tránh sử dụng default_scopenếu bạn có thể . Đó là vì lợi ích của chính bạn.


Câu trả lời này phải cao hơn
Stephen Corwin


5

Rails 3 default_scope dường như không bị ghi đè như đã làm trong Rails 2.

ví dụ

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"

Trong ứng dụng của tôi, sử dụng PostgreSQL, thứ tự trong phạm vi mặc định THẮNG. Tôi đang xóa tất cả default_scopes của mình và mã hóa nó rõ ràng ở mọi nơi.

Cạm bẫy Rails3!


1
Bạn phải sử dụngBar.foos.reorder(:created_at => :asc)
Ivan Stana

5

Với Rails 3+, bạn có thể sử dụng kết hợp không theo tỷ lệ và hợp nhất:

# model User has a default scope
query = User.where(email: "foo@example.com")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)

Điều này cũng làm việc cho tôi, để gọi không bị chặn trước (Rails 4.2):User.unscoped.where(email: "foo@example.com")
Nick

3

Trên Rails 5.1+ (và có thể sớm hơn, nhưng tôi đã thử nó hoạt động trên 5.1), có thể hủy bỏ một cột cụ thể, imho là giải pháp lý tưởng để loại bỏ default_scopemột kiểu thời trang có thể được sử dụng trong phạm vi được đặt tên. Trong trường hợp của OP default_scope,

Foo.unscope(where: :bar)

Hoặc là

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

Cả hai sẽ dẫn đến một truy vấn sql không áp dụng phạm vi ban đầu, nhưng không áp dụng bất kỳ điều kiện nào khác được sáp nhập vào arel.


2

Vâng, bạn luôn có thể sử dụng yêu thích thời gian cũ find_by_sqlvới truy vấn đầy đủ. Ví dụ: Model.find_by_sql ("CHỌN * TỪ các mô hình WHERE id = 123")

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.