Mengubah tipe kolom menjadi string yang lebih panjang dalam rel

90

Pada migrasi pertama, saya mendeklarasikan pada kolom contentmenjadi string Activerecord membuatnya menjadi string (255) menurut permata beranotasi.

Setelah saya mendorong aplikasi ke heroku, yang menggunakan postgres, jika saya memasukkan dalam bentuk konten string lebih panjang dari 255 saya mendapatkan kesalahan

PGError: ERROR: value too long for type character varying(255)

Masalahnya adalah saya perlu konten tersebut berisi string yang mungkin sangat panjang (teks bebas, bisa ribuan karakter)

  1. Variabel apa (apakah string tidak sesuai untuk ini) yang akan diterima pg?
  2. Bagaimana cara membuat migrasi untuk mengganti tipe kolom tersebut

Terima kasih

Nick Ginanto
sumber

Jawaban:

217

Anda harus menggunakan textdengan Rails jika Anda menginginkan string tanpa batas panjang. Migrasi seperti ini:

def up
  change_column :your_table, :your_column, :text
end
def down
  # This might cause trouble if you have strings longer
  # than 255 characters.
  change_column :your_table, :your_column, :string
end

harus menyelesaikan masalah. Anda mungkin ingin :null => falseatau beberapa opsi lain di akhir itu juga.

Saat Anda menggunakan stringkolom tanpa batas eksplisit, Rails akan menambahkan implisit :limit => 255. Tetapi jika Anda menggunakan text, Anda akan mendapatkan jenis string panjang apa pun yang didukung database. PostgreSQL memungkinkan Anda untuk menggunakan varcharkolom tanpa panjang tetapi sebagian besar database menggunakan tipe terpisah untuk itu dan Rails tidak tahu tentang varchartanpa panjang. Anda harus menggunakan textdi Rails untuk mendapatkan textkolom di PostgreSQL. Tidak ada perbedaan dalam PostgreSQL antara kolom jenis textdan satu jenis varchar(tapi varchar(n) yang berbeda). Selain itu, jika Anda menerapkan di atas PostgreSQL, tidak ada alasan untuk menggunakan :string(AKA varchar) sama sekali, database memperlakukan textdanvarchar(n)yang sama secara internal kecuali untuk batasan panjang ekstra untuk varchar(n); Anda sebaiknya hanya menggunakan varchar(n)(AKA :string) jika Anda memiliki batasan eksternal (seperti formulir pemerintah yang menyatakan bahwa bidang 432 pada formulir 897 / B akan memiliki panjang 23 karakter) pada ukuran kolom.

Selain itu, jika Anda menggunakan stringkolom di mana saja, Anda harus selalu menentukan :limitsebagai pengingat bagi diri Anda sendiri bahwa ada batas dan Anda harus memiliki validasi dalam model untuk memastikan bahwa batas tersebut tidak terlampaui. Jika Anda melebihi batas, PostgreSQL akan mengeluh dan memunculkan pengecualian, MySQL akan memotong string atau mengeluh secara diam-diam (tergantung pada konfigurasi server), SQLite akan membiarkannya lewat sebagaimana adanya, dan database lain akan melakukan sesuatu yang lain (mungkin mengeluh) .

Selain itu, Anda juga harus mengembangkan, menguji, dan menerapkan di atas database yang sama (yang biasanya berupa PostgreSQL di Heroku), Anda bahkan harus menggunakan versi server database yang sama. Ada perbedaan lain antara database (seperti perilaku GROUP BY) yang tidak akan mengisolasi Anda dari ActiveRecord. Anda mungkin sudah melakukan ini tetapi saya pikir saya akan menyebutkannya juga.


Pembaruan : Versi ActiveRecord yang lebih baru mengerti varchartanpa batas, jadi, dengan PostgreSQL setidaknya, Anda dapat mengatakan:

change_column :your_table, :your_column, :string, limit: nil

untuk mengubah varchar(n)kolom menjadi varchar. textdan varcharmasih sama sejauh menyangkut PostgreSQL tetapi beberapa pembuat formulir akan memperlakukannya secara berbeda: varcharmendapat <input type="text">sementara textmendapat multi-baris <textarea>.

mu terlalu pendek
sumber
13
Jawaban yang bagus. Satu catatan: Rails saat ini tidak mendukung change_column dengan metode perubahan ( panduan.rubyonrails.org/migrations.html#using-the-change-method ); jika memori berfungsi, Anda akan membuat migrasi yang tidak dapat diubah jika Anda melakukannya. Lebih baik melakukannya dengan cara jadul dengan metode naik / turun.
poetmountain
@BourbonJockey: Masuk akal changejika tidak dapat secara otomatis membalikkan perubahan jenis dan Panduan Migrasi mengatakan bahwa "[metode perubahan] Metode ini lebih disukai untuk menulis migrasi konstruktif (menambahkan kolom atau tabel)" dan change_columnisn ' t dalam daftar yang Anda tunjuk jadi saya pikir Anda benar. Saya memperbaikinya untuk menggunakan up/ down(dengan peringatan di down), terima kasih atas perhatiannya.
mu terlalu pendek
4
Untuk referensi pembaca lain di masa mendatang, mengonversi dari string ke teks di Postgres di Heroku dengan cara ini TIDAK akan kehilangan data.
Marina Martin
2
@ Dennis: Mungkin "Anda harus mengembangkan, menguji, dan menerapkan menggunakan database yang sama" akan lebih akurat. Masalah yang biasa terjadi adalah bahwa orang menggunakan penyiapan SQLite default (konyol) Rails dan hal-hal menjadi berantakan ketika mereka menerapkan di atas sesuatu yang lain. PostgreSQL masih merupakan opsi default dan paling umum di Heroku, bukan?
mu terlalu pendek
3
Di samping catatan, asumsi Rails bahwa bidang dengan panjang yang tidak ditentukan harus 255 karakter adalah aneh. Hal ini tidak diperlukan dalam PostgreSQL untuk menggunakan texthanya untuk mendapatkan panjang tak terbatas; Anda bisa menggunakan tidak dibatasi varchar. Rails memberlakukan batas ganjil ini, bukan PostgreSQL.
Craig Ringer
8

Meskipun jawaban yang diterima sangat bagus, saya ingin menambahkan jawaban di sini yang semoga lebih baik berkaitan dengan pertanyaan poster asli bagian 2, untuk non ahli seperti saya.

  1. Bagaimana cara membuat migrasi untuk mengganti tipe kolom tersebut

menghasilkan migrasi perancah

Anda dapat menghasilkan migrasi untuk menahan perubahan Anda dengan mengetik di konsol Anda (cukup ganti tableuntuk nama tabel Anda, dan columnuntuk nama kolom Anda)

rails generate migration change_table_column

Ini akan menghasilkan migrasi kerangka di dalam folder aplikasi / db / migrate / Rails Anda. Migrasi ini adalah placeholder untuk kode migrasi Anda.

Misalnya saya ingin membuat migrasi untuk mengubah tipe kolom dari stringmenjadi text, dalam tabel bernama TodoItems:

class ChangeTodoItemsDescription < ActiveRecord::Migration
  def change
     # enter code here
     change_column :todo_items, :description, :text
  end
end

Menjalankan migrasi Anda

Setelah Anda memasukkan kode untuk mengubah kolom, jalankan saja:

rake db:migrate

Untuk menerapkan migrasi Anda. Jika Anda membuat kesalahan, Anda selalu dapat mengembalikan perubahan dengan:

rake db:rollack

Metode Naik dan Turun

Referensi jawaban yang diterima Updan Downmetode, bukan Changemetode yang lebih baru . Sejak rail 3.2 gaya lama Metode Naik dan Turun menyajikan beberapa keunggulan dibandingkan metode Perubahan yang lebih baru. 'Atas dan Bawah' hindari ActiveRecord::IrreversibleMigration exception. Sejak rilis Rails 4 Anda dapat menggunakan reversibleuntuk menghindari kesalahan ini:

class ChangeProductsPrice < ActiveRecord::Migration
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

Nikmati Rails :)

Tony Cronin
sumber