การแทนที่ Rails default_scope


155

ถ้าฉันมีรูปแบบ ActiveRecord :: Base พร้อมขอบเขตเริ่มต้น:

class Foo < ActiveRecord::Base

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

end

มีวิธีใดบ้างที่จะทำFoo.find โดยไม่ใช้default_scopeเงื่อนไขหรือไม่? คุณสามารถแทนที่ขอบเขตเริ่มต้นได้หรือไม่?

ผมจะมีความคิดว่าการใช้เริ่มต้น 'ในชื่อจะชี้ให้เห็นว่ามันเป็น overridable มิฉะนั้นก็จะถูกเรียกว่าสิ่งที่ต้องการglobal_scopeใช่มั้ย?


คำตอบ:


151

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

ดูคำถามนี้สำหรับรายละเอียดเพิ่มเติม


ขอขอบคุณสำหรับลิงก์ไปยังคำถามก่อนหน้านี้
Gareth

10
> อย่าใช้ default_scope เว้นแต่ว่าคุณจะต้องทำจริงๆ คำแนะนำที่ยอดเยี่ยม! ขอบคุณ!
installero

2
จริงอยู่ การใช้default_scopeอาจเป็นความคิดที่ดี แต่อาจทำให้เกิดอาการปวดหัวหลายครั้งในระหว่างอายุการใช้งานแอปของคุณ
thomax


1
คุณพูดเกินจริงไปหน่อย default_scopeเป็นเครื่องมือที่ยอดเยี่ยมและมีสถานการณ์ที่คุณสามารถทำได้ด้วยวิธีอื่น แต่default_scopeเป็นเพียงสิ่งที่ถูกต้อง ตัวอย่างเช่นเมื่อคุณมีProductรูปแบบที่มีการinactiveตั้งค่าสถานะdefault_scope { where inactive: false }เป็นสิ่งที่ดีที่สุดที่จะทำตั้งแต่ 99% หรือกรณีคุณจะไม่ต้องการแสดงผลิตภัณฑ์ที่ไม่ได้ใช้งาน จากนั้นคุณเพียงแค่โทรหาunscoped1% ที่เหลือซึ่งอาจเป็นแผงผู้ดูแลระบบ
pedrozath

215

ใน Rails 3:

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

58
สิ่งนี้มีผลข้างเคียงหากโพสต์ has_many แสดงความคิดเห็น Post.first.comments.unscoped ส่งคืนความคิดเห็นทั้งหมด
Enrico Carlesso

3
เรื่องนี้ทำให้ฉันเมาจริงๆสักพัก โดยเฉพาะอย่างยิ่งถ้าคุณวางสิ่งนี้ในวิธีการเรียนเช่น: def self.random; unscoped.order('rand()'); endunscoped ลบทั้งหมด sql ก่อนมันไม่ใช่แค่สิ่งที่อยู่ภายใต้ default_scope ในขณะที่คำตอบที่ถูกต้องทางเทคนิคให้ใช้ความระมัดระวังunstopped
Schneems

7
คำเตือน! การไม่สแกนจะไม่ลบ default_scope เท่านั้นมันถูกพูดไปแล้วในความคิดเห็นอื่น แต่มันสามารถทำให้ยุ่งกับสิ่งต่างๆได้
dsimard

15
กฎง่ายๆก็คือunscopedเมื่อมันสามารถติดตามนางแบบได้โดยตรงเช่นFoo.unscoped.blah()ก็โอเค แต่ไม่เคยFoo.blah().unscopedเลย
Grant Birchmeier

stackoverflow.com/questions/1834159/…ทำงานรอบผลข้างเคียงที่กล่าวถึงโดย Enrico
wbharding

107

หากสิ่งที่คุณต้องการก็คือการเปลี่ยนแปลงคำสั่งที่กำหนดไว้ในdefault_scopeคุณสามารถใช้วิธีการreorder

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

Foo.reorder('created_at asc')

รัน SQL ต่อไปนี้:

SELECT * FROM "foos" ORDER BY created_at asc

3
เคล็ดลับ: กำหนดขอบเขตเช่นนี้scope :without_default_order, -> { reorder("") }และคุณสามารถทำสิ่งต่าง ๆ เช่นFoo.without_default_order.order("created_at ASC")ในบางสถานการณ์มันอ่านได้ดีกว่า (อาจไม่ใช่สถานการณ์ที่แน่นอน แต่ฉันมี)
Henrik N

การสั่งซื้อใหม่ทำได้สำหรับฉัน ขอบคุณมาก!
Andre Zimpel

49

เนื่องจาก4.1คุณสามารถใช้ActiveRecord::QueryMethods#unscopeเพื่อต่อสู้กับขอบเขตเริ่มต้น:

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

มันเป็นอยู่ในปัจจุบันไปได้ที่จะสิ่งที่ชอบ:unscope:where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having

แต่ก็ยังคงโปรดหลีกเลี่ยงการใช้ของdefault_scopeถ้าคุณสามารถ มันเป็นของคุณเอง


คำตอบนี้ควรจะสูงกว่า
Stephen Corwin


5

Rails 3 default_scope ไม่ปรากฏว่ามีการเขียนทับเหมือนใน Rails 2

เช่น

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"

ในแอพของฉันใช้ PostgreSQL การเรียงลำดับในขอบเขตเริ่มต้น WINS ฉันจะลบ default_scopes ของฉันทั้งหมดและเข้ารหัสในทุกที่อย่างชัดเจน

หลุมพราง Rails3!


1
คุณต้องใช้Bar.foos.reorder(:created_at => :asc)
Ivan Stana

5

ด้วย Rails 3+ คุณสามารถใช้การรวมกันของการไม่มีขอบเขตและผสาน:

# 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)

สิ่งนี้ได้ผลกับฉันเช่นกันที่จะโทรแบบไม่ จำกัด ขอบเขต (Rails 4.2):User.unscoped.where(email: "foo@example.com")
Nick

3

บน Rails 5.1+ (และอาจก่อนหน้านี้ แต่ฉันทดสอบแล้วว่าใช้งานได้ใน 5.1) เป็นไปได้ที่จะแยกสัญญาณคอลัมน์เฉพาะซึ่ง imho เป็นทางออกที่ดีที่สุดสำหรับการลบdefault_scopeแบบที่สามารถใช้ภายในขอบเขตที่มีชื่อ ในกรณีของการตรวจการณ์ที่default_scope,

Foo.unscope(where: :bar)

หรือ

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

ทั้งสองจะส่งผลให้แบบสอบถาม sql ที่ไม่ได้ใช้ขอบเขตเดิม แต่จะใช้สิ่งที่เงื่อนไขอื่น ๆ ได้รับการรวมเข้ากับ arel


2

คุณสามารถใช้รายการโปรดแบบเก่าfind_by_sqlกับแบบสอบถามที่สมบูรณ์ได้ตลอดเวลา ตัวอย่างเช่น: Model.find_by_sql ("SELECT * จากโมเดล WHERE id = 123")

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.