Rails find_or_create_by มากกว่าหนึ่งคุณลักษณะ?


202

มีแอตทริบิวต์แบบไดนามิกที่มีประโยชน์ในบันทึกที่ใช้งานที่เรียกว่า find_or_create_by:

Model.find_or_create_by_<attribute>(:<attribute> => "")

แต่ถ้าฉันต้องการ find_or_create ด้วยแอตทริบิวต์มากกว่าหนึ่งรายการล่ะ

ว่าฉันมีรูปแบบการจัดการความสัมพันธ์ M: M ระหว่างกลุ่มและสมาชิกที่เรียกว่า GroupMember ฉันสามารถมีหลายกรณีที่ member_id = 4 แต่ฉันไม่ต้องการมากกว่าหนึ่งครั้งที่ member_id = 4 และ group_id = 7 ฉันกำลังพยายามหาว่าเป็นไปได้ไหมที่จะทำสิ่งนี้:

GroupMember.find_or_create(:member_id => 4, :group_id => 7)

ฉันรู้ว่าอาจมีวิธีที่ดีกว่าในการจัดการกับสิ่งนี้ แต่ฉันชอบความสะดวกสบายของแนวคิดของ find_or_create

คำตอบ:


464

สามารถเชื่อมต่อกับหลายแอตทริบิวต์ด้วยand:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(ใช้find_or_initialize_byถ้าคุณไม่ต้องการบันทึกบันทึกทันที)

แก้ไข:วิธีการข้างต้นเลิกใช้งานใน Rails 4. วิธีการใหม่ในการดำเนินการคือ:

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create

และ

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize

แก้ไข 2:ไม่ใช่ทั้งหมดที่แยกออกจากรางเพียงแค่คุณลักษณะเฉพาะ

https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

ตัวอย่าง

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

กลายเป็น

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

คุณอาจชอบgithub.com/seamusabshere/upsertอาร์กิวเมนต์แรกคือแฮชของแอตทริบิวต์ที่ใช้ในการค้นหาหรือสร้างบันทึก
Seamus Abshere

1
มันจะมีประโยชน์ที่จะทราบว่าการเริ่มต้นเรียกสร้าง () แทนใหม่ () ที่อาจจะเรียกว่าในกรณีแรก _or_create
Sumit Bisht

บางคนสามารถแบ่งปันตัวอย่างสำคัญของการใช้งานนี้ได้ไหม ฉันไม่คุ้นเคยกับตำแหน่งและการโทร
6ft Dan

ฉันยกเลิกการลบรหัส Rails 3 เนื่องจากผู้คนจำนวนมากไม่ได้ใช้ Rails 4
Kyle Heironimus

เพียงเพิ่ม: find_or_create_by _ * () ฟังก์ชั่นได้รับการคัดค้านใน Rails 4 อย่างไรก็ตาม find_or_create_by () ไม่ใช่ ไม่เป็นไรหากใช้ find_or_create_by () ตรวจสอบการคอมมิชชันว่ามีการเพิ่มgithub.com/rails/rails/commit/ อย่างไรและเอกสารทางการของ Rails 4.2 สำหรับการใช้งาน: guide.rubyonrails.org/v4.2.0/ …
CodeExpress

33

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

# Return the first object which matches the attributes hash
# - or -
# Create new object with the given attributes
#
def self.find_or_create(attributes)
  Model.where(attributes).first || Model.create(attributes)
end

เคล็ดลับการเพิ่มประสิทธิภาพ:ไม่ว่าคุณจะเลือกโซลูชันแบบใดให้พิจารณาเพิ่มดัชนีสำหรับแอตทริบิวต์ที่คุณค้นหาบ่อยที่สุด


8
สิ่งหนึ่งที่ควรทราบคือสิ่งนี้จะไม่จัดการแอตทริบิวต์ที่ไม่สามารถกำหนดค่าโดยรวมfind_or_create_byได้
x1a4

1
Model.where(attributes).instance_eval{|q| q.first || q.create}มาร์โกลอง
ฮิโรชิ

3
find_or_createนอกจากนี้ยังมีประโยชน์ในการใช้การทำธุรกรรมซึ่งจะwhere….first || createนำเสนอสภาพการแข่งขัน
mrm

ฉันจะบอก def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) endว่าคุณไม่จำเป็นต้องพูดซ้ำModel
Augustin Riedinger

@AugustinRiedinger คุณไม่จำเป็นต้องselfใช้เนื้อความในเมธอด (เช่นคุณต้องการลบคำ!)
David Tuite

31

ใน Rails 4 คุณสามารถทำได้:

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

และการใช้งานwhereแตกต่างกัน:

GroupMember.where(member_id: 4, group_id: 7).first_or_create

นี้จะเรียกcreateในGroupMember.where(member_id: 4, group_id: 7):

GroupMember.where(member_id: 4, group_id: 7).create

ในทางตรงกันข้ามที่find_or_create_by(member_id: 4, group_id: 7)จะเรียกcreateในGroupMember:

GroupMember.create(member_id: 4, group_id: 7)

โปรดดูการกระทำที่เกี่ยวข้องนี้บนราง / ราง


16

โดยการส่งบล็อกไปยังfind_or_createคุณสามารถส่งพารามิเตอร์เพิ่มเติมที่จะถูกเพิ่มไปยังวัตถุหากมันถูกสร้างขึ้นใหม่ สิ่งนี้มีประโยชน์หากคุณตรวจสอบความถูกต้องของฟิลด์ที่คุณไม่ได้ค้นหา

สมมติว่า:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

แล้วก็

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }

จะสร้าง GroupMember ใหม่ด้วยชื่อ "John Doe" หากไม่พบด้วย member_id 4 and group_id 7


4

คุณทำได้:

User.find_or_create_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_create

หรือเพื่อเริ่มต้น:

User.find_or_initialize_by(first_name: 'Penélope', last_name: 'Lopez')
User.where(first_name: 'Penélope', last_name: 'Lopez').first_or_initialize

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