Rails find_or_create_by lebih dari satu atribut?


202

Ada atribut dinamis yang berguna dalam rekaman aktif bernama find_or_create_by:

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

Tetapi bagaimana jika saya perlu mencari_atau membuat lebih dari satu atribut?

Katakanlah saya memiliki model untuk menangani hubungan M: M antara Grup dan Anggota yang disebut GroupMember. Saya bisa memiliki banyak contoh di mana member_id = 4, tapi saya tidak pernah ingin lebih dari satu kali contoh di mana member_id = 4 dan group_id = 7. Saya mencoba mencari tahu apakah mungkin untuk melakukan sesuatu seperti ini:

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

Saya menyadari mungkin ada cara yang lebih baik untuk menangani ini, tapi saya suka kenyamanan ide find_or_create.

Jawaban:


464

Beberapa atribut dapat dihubungkan dengan and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(gunakan find_or_initialize_byjika Anda tidak ingin menyimpan catatan segera)

Sunting: Metode di atas tidak digunakan lagi dalam Rails 4. Cara baru untuk melakukannya adalah:

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

dan

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

Sunting 2: Tidak semua ini difaktorkan keluar dari rel hanya atribut khusus.

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

Contoh

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

menjadi

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

Anda mungkin juga menyukai github.com/seamusabshere/upsert - argumen pertama adalah hash atribut yang digunakan untuk menemukan atau membuat catatan
— Seamus Abshere

1
Penting untuk dicatat bahwa inisialisasi memanggil panggilan create () alih-alih new () yang mungkin dipanggil dalam kasus first_or_create
— Sumit Bisht

Bisakah seseorang membagikan contoh utama dari penggunaan ini? Saya tidak terbiasa dengan penempatan dan panggilannya.
— 6ft Dan

Saya mengembalikan penghapusan kode Rails 3, karena banyak orang belum menggunakan Rails 4.
— Kyle Heironimus

Hanya untuk menambahkan: find_or_create_by _ * () fungsi telah ditinggalkan dalam Rails 4, namun find_or_create_by () BUKAN. Tidak apa-apa menggunakan find_or_create_by (). Periksa komit di mana ini ditambahkan github.com/rails/rails/commit/… dan dokumentasi resmi Rails 4.2 untuk penggunaannya: guides.rubyonrails.org/v4.2.0/…
— CodeExpress

33

Untuk siapa pun yang menemukan sandaran ini tetapi perlu menemukan atau membuat objek dengan atribut yang mungkin berubah tergantung pada keadaan, tambahkan metode berikut ke model Anda:

# 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

Kiat pengoptimalan: apa pun solusi yang Anda pilih, pertimbangkan untuk menambahkan indeks untuk atribut yang paling sering Anda tanyakan.


8
Satu hal yang perlu diperhatikan adalah bahwa ini tidak akan menangani atribut yang tidak dapat ditetapkan secara massal, sementara find_or_create_byakan.
— x1a4

1
Marco, coba Model.where(attributes).instance_eval{|q| q.first || q.create}.
— hiroshi

3
find_or_createjuga memiliki manfaat menggunakan transaksi, di mana where….first || creatememperkenalkan kondisi balapan
— mrm

Saya akan mengatakan def self.find_or_create(attributes) self.where(attributes).first || self.create(attributes) endagar Anda tidak perlu mengulangModel
— Augustin Riedinger

@AugustinRiedinger Anda juga tidak perlu selfberada di dalam tubuh metode (karena Anda ingin menghapus kata-kata!).
— David Tuite

31

Di Rails 4 Anda bisa melakukan:

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

Dan penggunaannya whereberbeda:

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

Ini akan memanggil createpada GroupMember.where(member_id: 4, group_id: 7):

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

Sebaliknya, yang find_or_create_by(member_id: 4, group_id: 7)akan memanggil createpada GroupMember:

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

Silakan lihat komit yang relevan ini pada rel / rel.


16

Dengan meneruskan blok ke find_or_create, Anda bisa melewati parameter tambahan yang akan ditambahkan ke objek jika dibuat baru. Ini berguna jika Anda memvalidasi keberadaan bidang yang tidak Anda cari.

Asumsi:

class GroupMember < ActiveRecord::Base
    validates_presence_of :name
end

kemudian

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

akan membuat GroupMember baru dengan nama "John Doe" jika tidak menemukannya member_id 4 and group_id 7


4

Anda dapat melakukan:

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

Atau hanya menginisialisasi:

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.