ค้นหา vs find_by เทียบกับที่ไหน


127

ฉันยังใหม่กับราง สิ่งที่ฉันเห็นว่ามีหลายวิธีในการค้นหาบันทึก:

  1. find_by_<columnname>(<columnvalue>)
  2. find(:first, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>).first

และดูเหมือนว่าพวกเขาทั้งหมดจะสร้าง SQL เหมือนกันทุกประการ นอกจากนี้ฉันเชื่อว่าสิ่งเดียวกันนี้เป็นจริงสำหรับการค้นหาหลายระเบียน:

  1. find_all_by_<columnname>(<columnvalue>)
  2. find(:all, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>)

มีกฎง่ายๆหรือคำแนะนำว่าควรใช้ข้อใด?

คำตอบ:


103

ใช้อันไหนก็ได้ที่คุณคิดว่าเหมาะกับความต้องการของคุณที่สุด

findวิธีการมักจะถูกนำมาใช้ในการเรียกแถวโดย ID:

Model.find(1)

เป็นที่น่าสังเกตว่าfindจะมีข้อยกเว้นหากไม่พบรายการตามแอตทริบิวต์ที่คุณจัดหา ใช้where(ตามที่อธิบายไว้ด้านล่างซึ่งจะส่งคืนอาร์เรย์ว่างหากไม่พบแอตทริบิวต์) เพื่อหลีกเลี่ยงการเกิดข้อยกเว้น

การใช้งานอื่น ๆfindมักจะถูกแทนที่ด้วยสิ่งนี้:

Model.all
Model.first

find_byใช้เป็นตัวช่วยเมื่อคุณค้นหาข้อมูลภายในคอลัมน์และจะจับคู่กับหลักการตั้งชื่อ ตัวอย่างเช่นหากคุณมีชื่อคอลัมน์nameในฐานข้อมูลของคุณคุณจะใช้ไวยากรณ์ต่อไปนี้:

Model.find_by(name: "Bob")

.where เป็นมากกว่าการจับทั้งหมดที่ให้คุณใช้ตรรกะที่ซับซ้อนขึ้นเล็กน้อยเมื่อผู้ช่วยเหลือทั่วไปไม่ทำและส่งคืนอาร์เรย์ของรายการที่ตรงกับเงื่อนไขของคุณ (หรืออาร์เรย์ว่างเปล่า)


62
find_byไม่ได้เลิกใช้งาน แต่ไวยากรณ์มีการเปลี่ยนแปลงเล็กน้อย จากfind_by_name("Bob")ถึงfind_by(:name, "Bob").
Brian Morearty

61
@BrianMorearty ฉันเชื่อว่าคุณหมายถึงfind_by(name: "Bob")
MCB

1
@BrianMorearty ฉันไม่พบหลักฐานใด ๆ ว่าfind_by_...ถูกเลิกใช้งานคุณมีแหล่งที่มาหรือไม่? ดูเหมือนfind_byและfind_by_...ทั้งสองยังคงรองรับใน Rails 4
Dennis

4
@ เดนนิสคุณถูกต้องที่ไม่ได้เลิกใช้งานและนั่นคือสิ่งที่ฉันพูด แต่ฉันน่าจะชัดเจนกว่านี้เมื่อฉันพูดว่า "แต่ไวยากรณ์กำลังเปลี่ยนไปเล็กน้อย" สิ่งที่ฉันหมายถึงคือ "แต่ตอนนี้มีไวยากรณ์ใหม่แล้วเช่นกัน" ดูการแก้ไขของ MCB สำหรับไวยากรณ์ใหม่
Brian Morearty

3
นี่คือสิ่งที่กล่าวถึงในราง 4.0 รีลีส "วิธีการแบบไดนามิกทั้งหมดยกเว้น find_by _... และ find_by _... ! จะเลิกใช้" รายละเอียดเพิ่มเติมของedgeguides.rubyonrails.org/…
Mukesh Singh Rathaur

131

โดยที่ส่งคืน ActiveRecord :: Relation

ตอนนี้ดูการใช้งาน find_by:

def find_by
  where(*args).take
end

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


1
find_by ส่งคืนอ็อบเจ็กต์ แต่ที่ส่งคืนคอลเล็กชัน
Kick Buttowski

เมื่อค่าแบบสอบถามอยู่นอกช่วงfind_byจะช่วย::RangeErrorจากwhere(*args) และส่งคืนค่าศูนย์
fangxing

34

Model.find

1- พารามิเตอร์: ID ของวัตถุที่ต้องการค้นหา

2- หากพบ: จะส่งคืนวัตถุ (วัตถุเดียวเท่านั้น)

3- หากไม่พบ: มีActiveRecord::RecordNotFoundข้อยกเว้น

Model.find_by

1- พารามิเตอร์: คีย์ / ค่า

ตัวอย่าง:

User.find_by name: 'John', email: 'john@doe.com'

2- หากพบ: จะส่งคืนวัตถุ

3- nilถ้าไม่พบผลตอบแทน

หมายเหตุ:หากคุณต้องการเพิ่มการActiveRecord::RecordNotFoundใช้งานfind_by!

Model.where

1- พารามิเตอร์: เหมือนกับ find_by

2- หากพบ: จะส่งคืนข้อมูลActiveRecord::Relationที่มีหนึ่งระเบียนหรือมากกว่าที่ตรงกับพารามิเตอร์

3- ถ้าไม่ได้พบ: ActiveRecord::Relationมันกลับว่างเปล่า


31

มีความแตกต่างระหว่างfindและfind_byในที่findจะส่งคืนข้อผิดพลาดหากไม่พบในขณะที่find_byจะส่งคืนค่าว่าง

บางครั้งมันเป็นเรื่องง่ายที่จะอ่านถ้าคุณมีวิธีการเหมือนเมื่อเทียบกับfind_by email: "haha".where(email: some_params).first


17

ตั้งแต่ Rails 4 คุณสามารถทำได้:

User.find_by(name: 'Bob')

ซึ่งเทียบเท่าfind_by_nameใน Rails 3

ใช้#whereเมื่อใด#findและ#find_byไม่เพียงพอ


2
Agis ฉันเห็นด้วยกับคุณ แต่ฉันได้ค้นหาทางอินเทอร์เน็ตว่าทำไมเราจึงควรใช้find_byและไม่find_by_<column_name>ใช้ ฉันต้องการคำตอบสำหรับใครบางคน
KULKING

Agis คุณมีแหล่งข้อมูลสำรองที่เราไม่ควรใช้find_by_nameใน Rails 4 อีกต่อไปหรือไม่? เท่าที่ผมรู้ว่ามันไม่ได้รับการคัดค้าน
Dennis

มีวิธีง่ายๆในการทำเช่นนั้น แต่พูดว่าชื่อมีสัญลักษณ์แทนอยู่หรือไม่เช่นfind_by(name: "Rob*")
Batman

1
@ เดนนิสเป็นไปได้ที่จะใช้ทั้งสองอย่างถูกต้อง ฉันชอบไวยากรณ์ใหม่มากกว่าเนื่องจาก API เป็น IMHO ที่ใช้งานง่ายกว่า เป็นวิธีที่ฉันจะออกแบบเอง :)
Agis

8

คำตอบที่ยอมรับโดยทั่วไปครอบคลุมทุกอย่าง แต่ฉันต้องการเพิ่มบางอย่างในกรณีที่คุณวางแผนที่จะทำงานกับโมเดลในลักษณะเช่นการอัปเดตและคุณกำลังเรียกข้อมูลระเบียนเดียว (ซึ่งidคุณไม่รู้จัก) จากนั้นก็find_byคือ วิธีที่จะไปเพราะมันดึงข้อมูลและไม่ได้ใส่ไว้ในอาร์เรย์

irb(main):037:0> @kit = Kit.find_by(number: "3456")
  Kit Load (0.9ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" = 
 '3456' LIMIT 1
=> #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",   
updated_at: "2015-05-12 06:10:56", job_id: nil>

irb(main):038:0> @kit.update(job_id: 2)
(0.2ms)  BEGIN Kit Exists (0.4ms)  SELECT 1 AS one FROM "kits" WHERE  
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE  "kits"."id" = 
1  [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]] 
(0.6ms)  COMMIT => true

แต่ถ้าคุณใช้whereแล้วคุณจะไม่สามารถอัปเดตได้โดยตรง

irb(main):039:0> @kit = Kit.where(number: "3456")
Kit Load (1.2ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" =  
'3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456", 
created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58", 
job_id: 2>]>

irb(main):040:0> @kit.update(job_id: 3)
ArgumentError: wrong number of arguments (1 for 2)

ในกรณีนี้คุณจะต้องระบุเช่นนี้

irb(main):043:0> @kit[0].update(job_id: 3)
(0.2ms)  BEGIN Kit Exists (0.6ms)  SELECT 1 AS one FROM "kits" WHERE 
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1  
[["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
(0.5ms)  COMMIT => true

สิ่งที่ฉันกำลังมองหา
Paul Brunache

2
@kit = Kit.where (หมายเลข: "3456") ก่อน - ซึ่งจะช่วยให้คุณอัปเดตได้โดยตรงและปลอดภัยยิ่งขึ้นเนื่องจากรอดจากการเลิกใช้งาน
Paul Brunache

6

นอกเหนือจากคำตอบที่ได้รับการยอมรับแล้วสิ่งต่อไปนี้ก็ใช้ได้เช่นกัน

Model.find()สามารถยอมรับอาร์เรย์ของรหัสและจะส่งคืนระเบียนทั้งหมดที่ตรงกัน Model.find_by_id(123)ยอมรับอาร์เรย์ด้วย แต่จะประมวลผลเฉพาะค่า id แรกที่มีอยู่ในอาร์เรย์

Model.find([1,2,3])
Model.find_by_id([1,2,3])

5

ทั้ง # 2 ในรายการของคุณกำลังเลิกใช้งาน คุณยังสามารถfind(params[:id])ใช้ได้

โดยทั่วไปใช้where()งานได้ในสถานการณ์ส่วนใหญ่

นี่คือโพสต์ที่ยอดเยี่ยม: http://m.onkey.org/active-record-query-interface


1
ไม่มีโพสต์ที่ดี
Nithin


3

คำตอบที่ให้มานั้นใช้ได้ทั้งหมด

อย่างไรก็ตามความแตกต่างที่น่าสนใจอย่างหนึ่งคือModel.findการค้นหาด้วยรหัส หากพบมันจะส่งคืนModelวัตถุ (เป็นเพียงระเบียนเดียว) แต่จะโยนActiveRecord::RecordNotFoundไฟล์.

Model.find_byคล้ายกันมากModel.findและให้คุณค้นหาคอลัมน์หรือกลุ่มคอลัมน์ใดก็ได้ในฐานข้อมูลของคุณ แต่จะส่งคืนnilหากไม่มีบันทึกที่ตรงกับการค้นหา

Model.whereในทางกลับกันจะส่งคืนModel::ActiveRecord_Relationวัตถุซึ่งเป็นเช่นเดียวกับอาร์เรย์ที่มีระเบียนทั้งหมดที่ตรงกับการค้นหา หากไม่พบบันทึกจะส่งคืนModel::ActiveRecord_Relationวัตถุว่างเปล่า

ฉันหวังว่าสิ่งเหล่านี้จะช่วยคุณในการตัดสินใจว่าจะใช้เมื่อใด


3

สมมติว่าฉันมีนางแบบ User

  1. User.find(id)

ส่งคืนแถวที่คีย์หลัก = id ประเภทผลตอบแทนจะเป็นUserวัตถุ

  1. User.find_by(email:"abc@xyz.com")

ส่งคืนแถวแรกที่มีแอตทริบิวต์หรืออีเมลที่ตรงกันในกรณีนี้ ประเภทผลตอบแทนจะเป็นUserวัตถุอีกครั้ง

หมายเหตุ: - User.find_by(email: "abc@xyz.com")คล้ายกับUser.find_by_email("abc@xyz.com")

  1. User.where(project_id:1)

ส่งคืนผู้ใช้ทั้งหมดในตารางผู้ใช้ที่แอตทริบิวต์ตรงกัน

ประเภทผลตอบแทนที่นี่จะเป็นActiveRecord::Relationวัตถุ ActiveRecord::RelationคลาสรวมถึงEnumerableโมดูลของ Ruby เพื่อให้คุณสามารถใช้อ็อบเจกต์เช่นอาร์เรย์และสำรวจได้


0

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

find_by ~> ค้นหาระเบียนแรกที่ตรงกับเงื่อนไขที่ระบุ ไม่มีการสั่งซื้อโดยนัยดังนั้นหากคำสั่งมีความสำคัญคุณควรระบุด้วยตัวเอง หากไม่พบเร็กคอร์ดให้คืนค่า nil

find ~> ค้นหาเร็กคอร์ดแรกที่ตรงกับเงื่อนไขที่ระบุ แต่หากไม่พบเร็กคอร์ดจะทำให้เกิดข้อยกเว้น แต่จะทำโดยเจตนา

ตรวจสอบลิงก์ด้านบนซึ่งมีคำอธิบายและกรณีการใช้งานทั้งหมดสำหรับสองฟังก์ชันต่อไปนี้


-5

ฉันจะแนะนำให้ใช้เป็นการส่วนตัว

where(< columnname> => < columnvalue>)

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