Validasi keunikan beberapa kolom

193

Apakah ada cara rel cara untuk memvalidasi bahwa catatan aktual itu unik dan bukan hanya kolom? Misalnya, model / tabel pertemanan tidak boleh memiliki beberapa catatan identik seperti:

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20
re5et
sumber
7
maafkan saya jika saya padat, tetapi bagaimana itu membantu dalam situasi ini?
Ulangi
2
coba gunakan "validates_uniqueness_of" di model Anda. jika ini tidak berhasil, cobalah membuat indeks tempat Anda dapat membuat migrasi feild yang menyertakan pernyataan seperti add_index: table, [: column_a,: column_b],: unique => true)
Harry Joy
1
@ HarryJoy, dia bertanya Is there a rails-way way. Dan Anda menawarkan cara non-rel, tapi standar. The Active Record way claims that intelligence belongs in your models, not in the database.
Hijau
2
Sayangnya validates :field_name, unique: truerentan terhadap kondisi balapan, jadi meskipun melawan jalur rel, kendala yang sebenarnya lebih disukai. @ HarryJoy Saya akan menjawab dengan jawaban yang menjelaskan cara kendala.
Pooyan Khosravi
1
jawaban yang lebih baik maka semua yang tercantum di bawah ini adalah ini stackoverflow.com/a/34425284/1612469 karena ini membawa lapisan lain untuk memastikan semuanya akan bekerja dengan benar
Aleks

Jawaban:

319

Anda dapat mengatur validates_uniqueness_ofpanggilan sebagai berikut.

validates_uniqueness_of :user_id, :scope => :friend_id
Dylan Markow
sumber
83
Hanya ingin menambahkan bahwa Anda dapat melewati beberapa ruang lingkup jika Anda perlu memvalidasi keunikan pada lebih dari 2 bidang. Yaitu: scope => [: friend_id,: group_id]
Dave Rapin
27
Aneh yang tidak bisa Anda katakan validates_uniqueness_of [:user_id, :friend_id]. Mungkin ini perlu ditambal?
Alexey
12
Alexey, validates_uniqueness_of [: user_id,: friend_id] hanya akan melakukan validasi untuk masing-masing bidang yang terdaftar - dan itu didokumentasikan dan perilaku yang diharapkan
Nikita Hismatov
71
Di Rails 4, ini menjadi: memvalidasi: user_id, keunikan: {scope:: friend_id}
Marina Martin
3
Anda mungkin ingin menambahkan pesan kesalahan khusus seperti,: message => 'sudah memiliki teman ini.'
laffuste
137

Anda dapat menggunakan validatesuntuk memvalidasi uniquenesspada satu kolom:

validates :user_id, uniqueness: {scope: :friend_id}

Sintaks untuk validasi pada beberapa kolom serupa, tetapi Anda harus menyediakan array bidang sebagai gantinya:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}

Namun , pendekatan validasi yang ditunjukkan di atas memiliki kondisi balapan dan tidak dapat memastikan konsistensi. Perhatikan contoh berikut:

  1. catatan tabel database seharusnya unik oleh n bidang;

  2. beberapa ( dua atau lebih ) permintaan bersamaan, masing-masing ditangani oleh proses yang terpisah ( server aplikasi, server pekerja latar belakang atau apa pun yang Anda gunakan ), mengakses database untuk menyisipkan catatan yang sama dalam tabel;

  3. setiap proses secara paralel memvalidasi jika ada catatan dengan bidang n yang sama ;

  4. validasi untuk setiap permintaan berhasil dilewati, dan setiap proses membuat catatan dalam tabel dengan data yang sama.

Untuk menghindari perilaku semacam ini, kita harus menambahkan batasan unik ke tabel db. Anda dapat mengaturnya dengan add_indexhelper untuk satu (atau beberapa) bidang dengan menjalankan migrasi berikut:

class AddUniqueConstraints < ActiveRecord::Migration
  def change
   add_index :table_name, [:field1, ... , :fieldn], unique: true
  end
end

Peringatan : bahkan setelah Anda menetapkan batasan unik, dua atau lebih permintaan bersamaan akan mencoba untuk menulis data yang sama ke db, tetapi alih-alih membuat rekaman duplikat, ini akan menimbulkan ActiveRecord::RecordNotUniquepengecualian, yang harus Anda tangani secara terpisah:

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 
potashin
sumber