เหตุใดการใช้ราง default_scope จึงมักแนะนำให้ใช้


127

ทุกที่ บน อินเทอร์เน็ตคนพูดถึงว่าการใช้ทางรถไฟเป็นความคิดที่ไม่ดีและความนิยมสูงสุดสำหรับใน StackOverflow มีเกี่ยวกับวิธีการเขียนทับมัน สิ่งนี้ทำให้รู้สึกสับสนและมีคำถามที่ชัดเจน (ฉันคิดว่า)default_scopedefault_scope

เหตุใดจึงdefault_scopeแนะนำให้ใช้รางด้วย?

คำตอบ:


192

ปัญหา 1

ลองพิจารณาตัวอย่างพื้นฐาน:

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

แรงจูงใจในการตั้งค่าเริ่มต้นpublished: trueอาจเป็นเพื่อให้แน่ใจว่าคุณจะต้องถูกเปิดเผยเมื่อต้องการแสดงโพสต์ที่ไม่ได้เผยแพร่ (ส่วนตัว) จนถึงตอนนี้ดีมาก

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

นี่เป็นสิ่งที่เราคาดหวังมาก ตอนนี้ให้ลอง:

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

และที่นั่นเรามีปัญหาใหญ่ประการแรกเกี่ยวกับขอบเขตเริ่มต้น:

=> default_scope จะส่งผลต่อการเริ่มต้นโมเดลของคุณ

ในอินสแตนซ์ที่สร้างขึ้นใหม่ของแบบจำลองดังกล่าวdefault_scopeจะมีการแสดงผล ดังนั้นในขณะที่คุณอาจต้องการให้แน่ใจว่าจะไม่แสดงรายการโพสต์ที่ไม่ได้เผยแพร่โดยบังเอิญตอนนี้คุณกำลังสร้างโพสต์ที่เผยแพร่ตามค่าเริ่มต้น

ปัญหา 2

ลองพิจารณาตัวอย่างที่ละเอียดยิ่งขึ้น:

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

class User < ActiveRecord::Base
  has_many :posts
end

ให้ผู้ใช้รายแรกโพสต์:

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]]

ดูเหมือนว่าคาดไว้ (อย่าลืมเลื่อนไปทางขวาจนสุดเพื่อดูส่วนที่เกี่ยวกับ user_id)

ตอนนี้เราต้องการรับรายการโพสต์ทั้งหมด - รวมที่ไม่ได้เผยแพร่ - พูดสำหรับมุมมองของผู้ใช้ที่ล็อกอิน คุณจะรู้ว่าคุณต้อง 'เขียนทับ' หรือ 'เลิกทำ' ผลของdefault_scope. หลังจากที่ Google unscopedอย่างรวดเร็วคุณมีแนวโน้มที่จะหาข้อมูลเกี่ยวกับ ดูว่าจะเกิดอะไรขึ้นต่อไป:

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

=> Unscoped จะลบขอบเขตทั้งหมดที่โดยปกติอาจใช้กับการเลือกของคุณรวมถึงการเชื่อมโยง (แต่ไม่ จำกัด เพียง)

มีหลายวิธีในการเขียนทับเอฟเฟกต์ต่างๆของไฟล์default_scope. การได้รับสิทธิ์นั้นซับซ้อนอย่างรวดเร็วและฉันจะเถียงว่าไม่ใช้default_scopeตั้งแต่แรกจะเป็นทางเลือกที่ปลอดภัยกว่า


2
ในการกอง: ครั้งเดียวที่ฉันพบว่า default_scope มีประโยชน์คือเมื่อคุณต้องการโหลดการเชื่อมโยงโดยค่าเริ่มต้นอย่างมาก default_scope {eager_load ([: category,: comments])} แต่ !!! หากคุณกำลังทำการค้นหา count ในโมเดลนี้เช่น Product.count มันจะ eager_load การเชื่อมโยงสำหรับผลิตภัณฑ์ทั้งหมด และถ้าคุณมีระเบียน 50K การสืบค้นการนับของคุณก็เปลี่ยนไปจาก 15ms เป็น 500ms เพราะในขณะที่สิ่งที่คุณต้องการคือการนับ default_scope ของคุณจะเหลือทุกอย่าง
konung

16
สำหรับฉันแล้วดูเหมือนว่าจะมีปัญหาunscopedแทนที่จะเป็นdefault_scopeปัญหา # 2
กัปตัน Fogetti

4
@CaptainFogetti แน่นอน ฉันยังคิดว่าเป็นความคิดที่ดีที่จะนำเสนอข้อเสียของการ unscoped ซึ่งเป็นข้อเสียที่เป็นไปได้ของ default_scope ในกรณีส่วนใหญ่ที่ไม่สำคัญโดยใช้ default_scope จะทำให้คุณต้องใช้ unscoped นี่เป็นข้อแม้ระดับที่สอง (หากไม่มีคำที่ดีกว่า) ซึ่งง่ายต่อการพลาดเมื่อค้นคว้าวิธีการ
wrtsprt

1
ปัญหาเกี่ยวกับกรณีการใช้งานในคำตอบของคุณคือมีหลายกรณีที่คุณต้องการค้นหาโพสต์ที่ไม่ได้เผยแพร่ อันที่จริงฉันจะโต้แย้งว่าการค้นหาโพสต์ที่เผยแพร่เป็นกรณีพิเศษ เวลาเดียวที่คุณต้องการให้โพสต์เผยแพร่คือเมื่อมีคนดูเพจสาธารณะ แต่มีหลายครั้งที่คุณต้องการดูโพสต์ที่ไม่ได้เผยแพร่
B Seven

3
ผมคิดว่าการใช้งานที่ดีของการเป็นเมื่อคุณต้องการสิ่งที่จะถูกจัดเรียง:default_scope default_scope { order(:name) }
user2985898

9

อีกเหตุผลหนึ่งที่จะไม่ใช้default_scopeคือเมื่อคุณลบอินสแตนซ์ของโมเดลที่มีความสัมพันธ์default_scopeแบบ1 ต่อหลายกับโมเดล

พิจารณาตัวอย่าง:

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

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

โทรuser.destroyจะลบข้อความทั้งหมดที่มีแต่มันจะโพสต์ไม่ได้ลบที่มีpublished unpublishedดังนั้นฐานข้อมูลจะโยนการละเมิดคีย์ต่างประเทศเนื่องจากมีบันทึกที่อ้างอิงผู้ใช้ที่คุณต้องการลบ


6

มักจะแนะนำให้ใช้ default_scope เนื่องจากบางครั้งมีการใช้อย่างไม่ถูกต้องเพื่อ จำกัด ชุดผลลัพธ์ การใช้ default_scope ให้ดีคือการจัดลำดับชุดผลลัพธ์

ฉันจะอยู่ห่างจากการใช้wheredefault_scope และสร้างขอบเขตสำหรับสิ่งนั้น


1
ปัญหาที่สอง "Unscoped ลบขอบเขตทั้งหมดที่ปกติอาจใช้กับการเลือกของคุณรวมถึง (แต่ไม่ จำกัด เพียง) การเชื่อมโยง" ยังคงมีอยู่แม้ว่าdefault_scopeจะมีเพียงขอบเขตเดียวorderก็ตาม พฤติกรรมunscopedนี้ค่อนข้างคาดไม่ถึง
Zack Xu

1

สำหรับฉันก็คือไม่ความคิดที่ดีแต่ต้องใช้ด้วยความระมัดระวัง !. มีบางกรณีที่ฉันต้องการซ่อนบางระเบียนเมื่อตั้งค่าเขตข้อมูล

  1. เฉพาะอย่างยิ่งdefault_scopeจะต้องตรงกับค่าเริ่มต้น DB (เช่น{ where(hidden_id: nil) })
  2. เมื่อคุณแน่ใจโดยสิ้นเชิงว่าต้องการแสดงบันทึกเหล่านั้นมีunscopedวิธีการที่จะหลีกเลี่ยงไฟล์default_scope

ดังนั้นมันจะขึ้นอยู่กับความต้องการที่แท้จริง


0

ฉันพบว่าdefault_scopeมีประโยชน์ในการสั่งให้พารามิเตอร์บางตัวอยู่ในascหรือdescเรียงลำดับในทุกสถานการณ์เท่านั้น มิฉะนั้นฉันจะหลีกเลี่ยงมันเช่นโรคระบาด

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