Migrasi rel untuk kolom perubahan

327

Kami memiliki script/generate migration add_fieldname_to_tablename fieldname:datatypesintaks untuk menambahkan kolom baru ke model.

Pada baris yang sama, apakah kita memiliki skrip / hasil untuk mengubah tipe data kolom? Atau haruskah saya menulis SQL langsung ke migrasi vanilla saya?

Saya ingin mengubah kolom dari datetimemenjadi date.

papdel
sumber

Jawaban:

549

Saya pikir ini harus berhasil.

change_column :table_name, :column_name, :date
Alex Korban
sumber
13
@ b_ayan: sejauh yang saya tahu, satu-satunya kata ajaib dalam nama migrasi adalah "tambah" dan "hapus".
Alex Korban
1
Sort-of rails noob di sini, tapi ... Saya mengerti jawabannya tetapi bukan komentar pada jawaban ini. Klarifikasi dihargai :)
Alan H.
7
Saat Anda membuat migrasi, Anda memberinya nama (mis. Add_fieldname_to_tablename dalam pertanyaan di atas). Jika dimulai dengan "tambah" atau "hapus" maka migrasi akan secara otomatis diisi dengan kode untuk menambah atau menghapus kolom, yang menghemat Anda menulis kode itu sendiri.
Alex Korban
6
Perlu juga diingat bahwa Anda harus mengganti changetindakan yang biasa dengan tindakan terpisah updan downtindakan, seperti change_columnmigrasi yang tidak dapat dipulihkan dan akan meningkatkan kesalahan jika Anda harus mundur.
DaveStephens
1
@QPaysTaxes ke atas harus berisi dari mana Anda ingin mengubah kolom dari dan ke, dan ke bawah harus berisi cara membalikkan perubahan itu.
DaveStephens
98

Anda juga dapat menggunakan blok jika Anda memiliki beberapa kolom untuk berubah dalam tabel.

Contoh:

change_table :table_name do |t|
  t.change :column_name, :column_type, {options}
end

Lihat dokumentasi API pada kelas Tabel untuk detail lebih lanjut.

John
sumber
88

Saya tidak tahu apakah Anda dapat membuat migrasi dari baris perintah untuk melakukan semua ini, tetapi Anda dapat membuat migrasi baru, lalu mengedit migrasi untuk melakukan pengambilan ini.

Jika tablename adalah nama tabel Anda, fieldname adalah nama bidang Anda dan Anda ingin mengubah dari waktu ke tanggal, Anda dapat menulis migrasi untuk melakukan ini.

Anda dapat membuat migrasi baru dengan:

rails g migration change_data_type_for_fieldname

Kemudian edit migrasi untuk menggunakan change_table:

class ChangeDataTypeForFieldname < ActiveRecord::Migration
  def self.up
    change_table :tablename do |t|
      t.change :fieldname, :date
    end
  end
  def self.down
    change_table :tablename do |t|
      t.change :fieldname, :datetime
    end
  end
end

Kemudian jalankan migrasi:

rake db:migrate
Ryan
sumber
32

Seperti yang saya temukan pada jawaban sebelumnya, tiga langkah diperlukan untuk mengubah jenis kolom:

Langkah 1:

Hasilkan file migrasi baru menggunakan kode ini:

rails g migration sample_name_change_column_type

Langkah 2:

Buka /db/migratefolder dan edit file migrasi yang Anda buat. Ada dua solusi berbeda.

  1. def change
        change_column(:table_name, :column_name, :new_type)
    end

2.

    def up
        change_column :table_name, :column_name, :new_type
    end

    def down
        change_column :table_name, :column_name, :old_type
    end

Langkah 3:

Jangan lupa untuk melakukan perintah ini:

rake db:migrate

Saya telah menguji solusi ini untuk Rails 4 dan berfungsi dengan baik.

Aboozar Rajabi
sumber
1
Pada langkah 2, yang pertama akan gagal setelah menjalankan rake db: rollback, saya sarankan Anda memeriksa yang kedua
Feuda
Apakah ada konvensi rel yang memungkinkan semuanya menjadi lebih atau kurang dilakukan saat membuat file migrasi tanpa pergi ke sana, dan kemudian mengeditnya?
BKSpurgeon
@BKSpurgeon Ya, periksa dokumentasi di sini: edgeguides.rubyonrails.org/active_record_migrations.html
Aboozar Rajabi
12

Dengan Rails 5

Dari Rails Guides :

Jika Anda menginginkan migrasi untuk melakukan sesuatu yang Rekaman Aktif tidak tahu cara membalikkan, Anda dapat menggunakan reversible:

class ChangeTablenameFieldname < ActiveRecord::Migration[5.1]
  def change
    reversible do |dir|
      change_table :tablename do |t|
        dir.up   { t.change :fieldname, :date }
        dir.down { t.change :fieldname, :datetime }
      end
    end
  end
end
Tuan Tao
sumber
8

Cukup hasilkan migrasi:

rails g migration change_column_to_new_from_table_name

Perbarui migrasi seperti ini:

class ClassName < ActiveRecord::Migration
  change_table :table_name do |table|
    table.change :column_name, :data_type
  end
end

dan akhirnya

rake db:migrate
Vivek Sharma
sumber
2

Cara lain untuk mengubah tipe data menggunakan migrasi

langkah1: Anda harus menghapus nama bidang tipe data yang salah menggunakan migrasi

ex:

rails g migration RemoveFieldNameFromTableName field_name:data_type

Di sini jangan lupa untuk menentukan tipe data untuk bidang Anda

Langkah 2: Sekarang Anda bisa menambahkan bidang dengan tipe data yang benar

ex:

rails g migration AddFieldNameToTableName field_name:data_type

Itu saja, sekarang meja Anda akan ditambahkan dengan bidang tipe data yang benar, Selamat ruby ​​coding !!

prasanthrubyist
sumber
2

Ini semua dengan asumsi bahwa tipe data kolom memiliki konversi implisit untuk setiap data yang ada. Saya telah mengalami beberapa situasi di mana data yang ada, katakanlah a Stringdapat secara implisit dikonversi menjadi tipe data baru, katakanlah Date.

Dalam situasi ini, perlu diketahui bahwa Anda dapat membuat migrasi dengan konversi data. Secara pribadi, saya suka meletakkan ini di file model saya, dan kemudian menghapusnya setelah semua skema database telah dimigrasi dan stabil.

/app/models/table.rb
  ...
  def string_to_date
    update(new_date_field: date_field.to_date)
  end

  def date_to_string
    update(old_date_field: date_field.to_s)
  end
  ...
    def up
        # Add column to store converted data
        add_column :table_name, :new_date_field, :date
        # Update the all resources
        Table.all.each(&:string_to_date)
        # Remove old column
        remove_column :table_name, :date_field
        # Rename new column
        rename_column :table_name, :new_date_field, :date_field
    end

    # Reversed steps does allow for migration rollback
    def down
        add_column :table_name, :old_date_field, :string
        Table.all.each(&:date_to_string)
        remove_column :table_name, :date_field
        rename_column :table_name, :old_date_field, :date_field
    end
Sebastian Scholl
sumber
0

Untuk melengkapi jawaban jika mengedit nilai default :

Di konsol rel Anda:

rails g migration MigrationName

Dalam migrasi:

  def change
    change_column :tables, :field_name, :field_type, default: value
  end

Akan terlihat seperti :

  def change
    change_column :members, :approved, :boolean, default: true
  end
Gregdebrick
sumber