Bagaimana cara membuat kolom unik dan indeks dalam migrasi Ruby on Rails?


416

Saya ingin membuat kolom uniquedalam skrip migrasi Ruby on Rails. Apa cara terbaik untuk melakukannya? Apakah ada cara untuk mengindeks kolom dalam tabel?

Saya ingin menegakkan uniquekolom dalam database sebagai lawan dari hanya menggunakan :validate_uniqueness_of.

Jawaban:


686

Jawaban singkat untuk Rails versi lama (lihat jawaban lain untuk Rails 4+):

add_index :table_name, :column_name, unique: true

Untuk mengindeks beberapa kolom secara bersamaan, Anda meneruskan larik nama kolom alih-alih nama kolom tunggal,

add_index :table_name, [:column_name_a, :column_name_b], unique: true

Jika Anda mendapatkan "nama indeks ... terlalu panjang", Anda dapat menambahkan name: "whatever"metode add_index untuk membuat nama lebih pendek.

Untuk kontrol " execute" berbutir halus, ada metode " " yang mengeksekusi SQL langsung.

Itu dia!

Jika Anda melakukan ini sebagai pengganti validasi model lama yang biasa, periksa untuk melihat cara kerjanya. Kesalahan pelaporan kepada pengguna kemungkinan tidak akan sebaik tanpa validasi tingkat model. Anda selalu dapat melakukan keduanya.


36
+1 untuk menyarankan terus menggunakan validates_uniqueness_of. Penanganan kesalahan jauh lebih bersih menggunakan metode ini untuk biaya permintaan tunggal yang diindeks saya sarankan dia melakukan keduanya
Steve Weet

1
Saya mencoba bahwa itu tampaknya tidak berhasil! Saya bisa memasukkan dua catatan dengan nama_kolom yang saya definisikan unik! Saya menggunakan Rails 2.3.4 dan MySql ada ide?
Tam

Saya menggunakan saran kedua Anda dengan menggunakan execute: jalankan "pengguna ALTER TABLE ADD UNIK (email)" dan itu berhasil! tidak yakin mengapa yang pertama tidak akan tertarik untuk mengetahui
Tam

1
Jika Anda mendapatkan indexed columns are not uniquekesalahan saat mencoba membuat indeks unik, itu mungkin karena data dalam tabel sudah berisi duplikat. Coba hapus data duplikat dan jalankan migrasi lagi.
Hartley Brody

5
Jika Anda mendapatkan "nama indeks ... terlalu panjang", Anda dapat menambahkan , :name => "whatever"ke add_indexmetode untuk membuat nama yang lebih pendek.
Rick Smith

129

rails menghasilkan migrasi add_index_to_table_name column_name: uniq

atau

rails menghasilkan migrasi add_column_name_to_table_name column_name: string: uniq: index

menghasilkan

class AddIndexToModerators < ActiveRecord::Migration
  def change
    add_column :moderators, :username, :string
    add_index :moderators, :username, unique: true
  end
end

Jika Anda menambahkan indeks ke kolom yang ada, hapus atau komentari add_columnbaris, atau beri tanda centang

add_column :moderators, :username, :string unless column_exists? :moderators, :username

5
Saya membatalkan ini karena saya ingin bentuk baris perintah. Tapi itu konyol menambahkan kolom bahkan ketika saya tentukan add_index...dan tidak add_column....
Tyler Collier

1
Ya, mungkin di versi berikutnya.
d.danailov

49

Karena ini belum disebutkan tetapi menjawab pertanyaan yang saya miliki ketika saya menemukan halaman ini, Anda juga dapat menentukan bahwa indeks harus unik ketika menambahkannya melalui t.referencesatau t.belongs_to:

create_table :accounts do |t|
  t.references :user, index: { unique: true } # or t.belongs_to

  # other columns...
end

(setidaknya dari Rails 4.2.7)


42

Jika Anda membuat tabel baru, Anda bisa menggunakan pintasan inline:

  def change
    create_table :posts do |t|
      t.string :title, null: false, index: { unique: true }
      t.timestamps
    end
  end

14

Saya menggunakan Rails 5 dan jawaban di atas berfungsi dengan baik; inilah cara lain yang juga berfungsi untuk saya (nama tabel :peopledan nama kolomnya :email_address)

class AddIndexToEmailAddress < ActiveRecord::Migration[5.0]
  def change
    change_table :people do |t|
      t.index :email_address, unique: true
    end
  end
end

1

Anda mungkin ingin menambahkan nama untuk kunci unik karena berkali-kali nama unique_key default oleh rails bisa terlalu lama sehingga DB dapat melempar kesalahan.

Untuk menambahkan nama untuk indeks Anda cukup gunakan name:opsi. Kueri migrasi mungkin terlihat seperti ini -

add_index :table_name, [:column_name_a, :column_name_b, ... :column_name_n], unique: true, name: 'my_custom_index_name'

Info lebih lanjut - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index


1
add_index :table_name, :column_name, unique: true

Untuk mengindeks beberapa kolom secara bersamaan, Anda meneruskan larik nama kolom alih-alih satu nama kolom.


0

Jika Anda lupa menambahkan unik ke kolom DB, cukup tambahkan validasi ini dalam model untuk memeriksa apakah bidang tersebut unik:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

lihat di sini Di atas hanya untuk tujuan pengujian, silakan tambahkan indeks dengan mengubah kolom DB seperti yang disarankan oleh @Nate

silakan merujuk ini dengan indeks untuk informasi lebih lanjut


2
Saya tidak akan merekomendasikan hanya menambahkan validasi tanpa indeks yang sesuai. Opsi yang lebih baik adalah membersihkan duplikat yang ada dan kemudian menambahkan indeks. Kalau tidak, Anda berisiko membatalkan data yang ada (yang akan menyebabkan pembaruan pada baris-baris itu gagal), dan Anda masih bisa berakhir dengan duplikat jika Anda memiliki kode apa pun yang melewatkan validasi Rails. (misalnya, ketika menjalankan pembaruan_all, atau menyisipkan SQL langsung)
Nate
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.