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.