Cara memilih ID tempat di Array Rails ActiveRecord tanpa kecuali

135

Ketika saya memiliki array id, suka

ids = [2,3,5]

dan saya tampil

Comment.find(ids)

semuanya bekerja dengan baik. Tetapi ketika ada id yang tidak ada, saya mendapatkan pengecualian. Ini terjadi secara umum ketika saya mendapatkan daftar ID yang cocok dengan beberapa filter dan daripada saya melakukan sesuatu seperti

current_user.comments.find(ids)

Kali ini saya mungkin memiliki ID komentar yang valid, yang bagaimanapun bukan milik Pengguna yang diberikan, jadi tidak ditemukan dan saya mendapatkan pengecualian.

Saya sudah mencoba find(:all, ids), tetapi mengembalikan semua catatan.

Satu-satunya cara saya bisa melakukannya sekarang adalah

current_user.comments.select { |c| ids.include?(c.id) }

Tetapi bagi saya itu sepertinya solusi yang sangat tidak efisien.

Apakah ada cara yang lebih baik untuk memilih ID di Array tanpa mendapatkan pengecualian pada catatan yang tidak ada?

Jakub Arnold
sumber

Jawaban:

216

Jika hanya menghindari pengecualian yang Anda khawatirkan, keluarga fungsi "find_all_by .." bekerja tanpa melempar pengecualian.

Comment.find_all_by_id([2, 3, 5])

akan berfungsi bahkan jika beberapa id tidak ada. Ini berfungsi di

user.comments.find_all_by_id(potentially_nonexistent_ids)

kasus juga.

Pembaruan: Rails 4

Comment.where(id: [2, 3, 5])
prismofeverything
sumber
ini adalah solusi yang saya sukai, tampaknya lebih bersih daripada rute penanganan pengecualian
Sam Saffron
5
Sebagai ekstensi lain untuk ini, jika Anda perlu untuk menghubungkan kondisi yang rumit, Anda bahkan dapat melakukan Comment.all (: conditions => ["disetujui dan id dalam (?)", [1,2,3]])
Omar Qureshi
14
ini akan ditinggalkan dalam Rails 4: edgeguides.rubyonrails.org/…
Jonathan Lin
3
@ JonathanLin benar, jawaban mjnissim harus lebih disukai: stackoverflow.com/a/11457025/33226
Gavin Miller
6
Ini mengembalikan Arraybukan ActiveRecord::Relation, yang membatasi apa yang dapat Anda lakukan dengannya sesudahnya. Comment.where(id: [2, 3, 5])mengembalikan sebuah ActiveRecord::Relation.
Joshua Pinter
148

Pembaruan: Jawaban ini lebih relevan untuk Rails 4.x

Melakukan hal ini:

current_user.comments.where(:id=>[123,"456","Michael Jackson"])

Sisi kuat dari pendekatan ini adalah bahwa ia mengembalikan Relationobjek, di mana Anda dapat bergabung dengan lebih banyak .whereklausa, .limitklausa, dll., Yang sangat membantu. Ini juga memungkinkan ID yang tidak ada tanpa membuang pengecualian.

Sintaks Ruby yang lebih baru adalah:

current_user.comments.where(id: [123, "456", "Michael Jackson"])
mjnissim
sumber
Terima kasih telah mengonfirmasi wheresintaks saat membandingkan ke array. Saya pikir saya mungkin harus kode SQL dengan INpernyataan, tetapi ini terlihat lebih bersih dan merupakan pengganti yang mudah untuk yang sudah usang scoped_by_id.
Mark Berry
1
Apa ini disebut dan bagaimana cara kerjanya? Apakah ini Rails magic ?! Seperti komentar seorang kolega, ini seperti 'membandingkan bilangan bulat dengan daftar objek ".
atw
23

Jika Anda membutuhkan lebih banyak kontrol (mungkin Anda perlu menyebutkan nama tabel) Anda juga dapat melakukan hal berikut:

Model.joins(:another_model_table_name)
  .where('another_model_table_name.id IN (?)', your_id_array)
Jonathan Lin
sumber
Persis apa yang saya cari. Terima kasih!
Myxtic
Apakah ada cara untuk menjaga urutan your_id_arrayketika Anda mendapatkan kembali benda-benda itu?
Joshua Pinter
@ JoshPinter Saya tidak berpikir ini adalah cara yang dapat diandalkan untuk mengharapkan database mengembalikan barang dalam urutan yang sama. Mungkin menambahkan permintaan ORDER BY di akhir untuk memastikan urutan yang benar.
Jonathan Lin
@JonathanLin Terima kasih atas tanggapannya Jonathan. Anda tentu benar. Menggunakan tidak ORDER BYakan berfungsi dalam situasi saya karena pesanan tidak didasarkan pada atribut. Namun, ada cara untuk melakukannya melalui SQL (jadi cepat) dan seseorang bahkan telah membuat permata untuk itu. Lihat T&J ini: stackoverflow.com/questions/801824/…
Joshua Pinter
10

Sekarang .find dan .find_by_id sudah tidak digunakan lagi di rail 4. Jadi, kita bisa menggunakan di bawah ini:

Comment.where(id: [2, 3, 5])

Ini akan bekerja bahkan jika beberapa id tidak ada. Ini berfungsi di

user.comments.where(id: avoided_ids_array)

Juga untuk mengecualikan ID

Comment.where.not(id: [2, 3, 5])
Sumit Munot
sumber
3
Metode github.com/rails/activerecord-deprecated_finders .find dan .find_by_id TIDAK ditinggalkan dalam rail 4.
Canna
0

Untuk menghindari pengecualian yang membunuh aplikasi Anda, Anda harus menangkap pengecualian itu dan memperlakukannya seperti yang Anda inginkan, menentukan perilaku aplikasi Anda pada situasi di mana id tidak ditemukan.

begin
  current_user.comments.find(ids)
rescue
  #do something in case of exception found
end

Berikut info lebih lanjut tentang pengecualian di ruby.

rogeriopvl
sumber
1
ya ini memecahkan masalah, tapi itu bukan solusi yang bersih
Jakub Arnold
3
Jika Anda akan menangkap pengecualian, Anda harus mendeklarasikan pengecualian yang ingin Anda tangkap, jika tidak Anda berisiko menangkap sesuatu yang tidak Anda harapkan dan menyembunyikan masalah yang sebenarnya.
Haegin
0

Anda juga dapat menggunakannya dalam named_scope jika Anda ingin menempatkan kondisi lain di sana

misalnya termasuk beberapa model lain:

named_scope 'get_by_ids', lambda {| ids | {: include => [: comments],: conditions => ["comments.id IN (?)", id]}}

mtfk
sumber