Jawaban:
Pembantu cap waktu hanya tersedia di create_table
blok. Anda dapat menambahkan kolom ini dengan menentukan jenis kolom secara manual:
class AddTimestampsToUser < ActiveRecord::Migration
def change_table
add_column :users, :created_at, :datetime, null: false
add_column :users, :updated_at, :datetime, null: false
end
end
Meskipun ini tidak memiliki sintaks singkat yang sama dengan add_timestamps
metode yang telah Anda tentukan di atas, Rails masih akan memperlakukan kolom ini sebagai kolom timestamp, dan memperbarui nilai-nilai secara normal.
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime
- pintasan untuk menghasilkan migrasi di atas.
PG::NotNullViolation: ERROR: column "created_at" contains null value
karena tabel saya sudah berisi data yang melanggar bukan batasan nol. Adakah cara yang lebih baik untuk melakukan ini daripada menghapus contraint yang tidak nol pada awalnya dan kemudian menambahkannya nanti?
add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now
. Time.zone.now
hanyalah sebuah contoh, Anda harus menggunakan nilai apa pun yang masuk akal untuk logika Anda.
Migrasi hanyalah dua metode kelas (atau metode instance pada 3.1): up
dan down
(dan terkadang change
metode instance pada 3.1). Anda ingin perubahan Anda masuk ke up
metode:
class AddTimestampsToUser < ActiveRecord::Migration
def self.up # Or `def up` in 3.1
change_table :users do |t|
t.timestamps
end
end
def self.down # Or `def down` in 3.1
remove_column :users, :created_at
remove_column :users, :updated_at
end
end
Jika Anda berada di 3.1 maka Anda juga dapat menggunakan change
(terima kasih Dave):
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table(:users) { |t| t.timestamps }
end
end
Mungkin Anda membingungkan def change
, def change_table
dan change_table
.
Lihat panduan migrasi untuk detail lebih lanjut.
change
metode sekarang, meskipun dalam kasus ini, bukan masalahnya :)
change
patut disebutkan jadi saya akan menambahkannya juga.
Kode asli Anda sangat dekat dengan kanan, Anda hanya perlu menggunakan nama metode yang berbeda. Jika Anda menggunakan Rails 3.1 atau yang lebih baru, Anda perlu mendefinisikan change
metode alih-alih change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def change
add_timestamps(:users)
end
end
Jika Anda menggunakan versi yang lebih lama, Anda perlu mendefinisikan up
dan down
metode alih-alih change_table
:
class AddTimestampsToUser < ActiveRecord::Migration
def up
add_timestamps(:users)
end
def down
remove_timestamps(:users)
end
end
Tanggapan @ user1899434 menjawab pada fakta bahwa tabel "yang ada" di sini bisa berarti tabel dengan catatan yang sudah ada di dalamnya, catatan yang mungkin tidak ingin Anda jatuhkan. Jadi, ketika Anda menambahkan cap waktu dengan null: false, yang merupakan default dan sering diinginkan, semua catatan yang ada semuanya tidak valid.
Tapi saya pikir jawaban itu dapat ditingkatkan, dengan menggabungkan dua langkah menjadi satu migrasi, serta menggunakan metode add_timestamps yang lebih semantik:
def change
add_timestamps :projects, default: Time.zone.now
change_column_default :projects, :created_at, nil
change_column_default :projects, :updated_at, nil
end
Anda dapat mengganti stempel waktu lain untuk DateTime.now
, seperti jika Anda ingin catatan yang sudah ada dibuat / diperbarui pada waktu fajar sebagai gantinya.
Time.zone.now
adalah apa yang harus digunakan jika kita ingin kode kita mematuhi zona waktu yang benar.
Time.zone.now
yang akan mengembalikan contoh waktu yang dibuat ketika migrasi dijalankan dan cukup gunakan waktu itu sebagai default. Objek baru tidak akan mendapatkan instance Waktu baru.
class AddTimestampsToUser < ActiveRecord::Migration
def change
change_table :users do |t|
t.timestamps
end
end
end
Transformasi yang tersedia adalah
change_table :table do |t|
t.column
t.index
t.timestamps
t.change
t.change_default
t.rename
t.references
t.belongs_to
t.string
t.text
t.integer
t.float
t.decimal
t.datetime
t.timestamp
t.time
t.date
t.binary
t.boolean
t.remove
t.remove_references
t.remove_belongs_to
t.remove_index
t.remove_timestamps
end
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
Jawaban Nick Davies adalah yang paling lengkap dalam hal menambahkan kolom cap waktu ke tabel dengan data yang ada. Satu-satunya downside adalah bahwa ia akan naik ActiveRecord::IrreversibleMigration
pada db:rollback
.
Ini harus dimodifikasi agar berfungsi di kedua arah:
def change
add_timestamps :campaigns, default: DateTime.now
change_column_default :campaigns, :created_at, from: DateTime.now, to: nil
change_column_default :campaigns, :updated_at, from: DateTime.now, to: nil
end
change_column_default
tidak mendukung from
dan to
dalam versi itu?), Tapi saya mengambil ide ini dan menciptakan up/down
metode, bukan metode tunggal change
dan itu bekerja seperti pesona!
def change
add_timestamps :table_name
end
tidak yakin kapan tepatnya ini diperkenalkan, tetapi di rel 5.2.1 Anda bisa melakukan ini:
class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
def change
add_timestamps :my_table
end
end
untuk lebih lanjut lihat " menggunakan metode perubahan " di dokumentasi migrasi catatan aktif.
, null: true
setelah:my_table
Saya membuat fungsi sederhana yang dapat Anda panggil untuk ditambahkan ke setiap tabel (dengan asumsi Anda memiliki database yang sudah ada) bidang Created_at dan updated_at :
# add created_at and updated_at to each table found.
def add_datetime
tables = ActiveRecord::Base.connection.tables
tables.each do |t|
ActiveRecord::Base.connection.add_timestamps t
end
end
add_timestamps (table_name, options = {}) publik
Menambahkan kolom cap waktu (Created_at dan updated_at) ke table_name. Opsi tambahan (seperti null: false) diteruskan ke #add_column.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps(:users, null: false)
end
end
Jawaban sebelumnya tampaknya benar tetapi saya menghadapi masalah jika meja saya sudah memiliki entri.
Saya akan mendapatkan 'GALAT: kolom created_at
berisi null
nilai'.
Untuk memperbaikinya, saya menggunakan:
def up
add_column :projects, :created_at, :datetime, default: nil, null: false
add_column :projects, :updated_at, :datetime, default: nil, null: false
end
Saya kemudian menggunakan gem migration_data untuk menambahkan waktu untuk proyek saat ini pada migrasi seperti:
def data
Project.update_all created_at: Time.now
end
Kemudian semua proyek yang dibuat setelah migrasi ini akan diperbarui dengan benar. Pastikan server juga dinyalakan ulang sehingga Rails ActiveRecord
mulai melacak cap waktu pada catatan.
Banyak jawaban di sini, tetapi saya juga akan memposting jawaban saya karena tidak ada yang sebelumnya benar-benar bekerja untuk saya :)
Seperti yang telah dicatat oleh beberapa orang, #add_timestamps
sayangnya menambahkan null: false
batasan, yang akan menyebabkan baris lama menjadi tidak valid karena mereka tidak memiliki nilai-nilai ini. Sebagian besar jawaban di sini menyarankan agar kami menetapkan beberapa nilai default (Time.zone.now
), tetapi saya tidak ingin melakukannya karena cap waktu default untuk data lama ini tidak akan benar. Saya tidak melihat nilai dalam menambahkan data yang salah ke tabel.
Jadi migrasi saya hanyalah:
class AddTimestampsToUser < ActiveRecord::Migration
def change_table
add_column :projects, :created_at, :datetime
add_column :projects, :updated_at, :datetime
end
end
Tidak null: false
, tidak ada batasan lain. Baris lama akan terus valid dengan created_at
as NULL
, dan update_at
as NULL
(hingga beberapa pembaruan dilakukan ke baris). Baris baru akan memiliki created_at
dan updated_at
mengisi seperti yang diharapkan.
Masalah dengan sebagian besar jawaban di sini adalah bahwa jika Anda default ke Time.zone.now
semua catatan akan memiliki waktu migrasi dijalankan sebagai waktu default mereka, yang mungkin bukan yang Anda inginkan. Di rel 5 Anda bisa menggunakan now()
. Ini akan mengatur stempel waktu untuk catatan yang ada saat migrasi dijalankan, dan sebagai waktu mulai transaksi komit untuk catatan yang baru dimasukkan.
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps :users, default: -> { 'now()' }, null: false
end
end
Menggunakan Time.current
adalah gaya yang baik https://github.com/rubocop-hq/rails-style-guide#timenow
def change
change_table :users do |t|
t.timestamps default: Time.current
t.change_default :created_at, from: Time.current, to: nil
t.change_default :updated_at, from: Time.current, to: nil
end
end
atau
def change
add_timestamps :users, default: Time.current
change_column_default :users, :created_at, from: Time.current, to: nil
change_column_default :users, :updated_at, from: Time.current, to: nil
end
Ini sederhana untuk menambahkan cap waktu di tabel yang ada.
class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
def change
add_timestamps :custom_field_metadata
end
end
Bagi mereka yang tidak menggunakan Rails tetapi menggunakan activerecord, berikut ini juga menambahkan kolom ke model yang sudah ada, contohnya adalah untuk bidang integer.
ActiveRecord::Schema.define do
change_table 'MYTABLE' do |table|
add_column(:mytable, :my_field_name, :integer)
end
end
Ini sepertinya solusi bersih di Rails 5.0.7 (menemukan metode change_column_null):
def change
add_timestamps :candidate_offices, default: nil, null: true
change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
end
Saya di kereta 5.0 dan tidak ada opsi ini bekerja.
Satu-satunya hal yang berhasil adalah menggunakan tipe menjadi: timestamp dan bukan: datetime
def change
add_column :users, :created_at, :timestamp
add_column :users, :updated_at, :timestamp
end
Saya mengalami masalah yang sama pada Rails 5 mencoba menggunakan
change_table :my_table do |t|
t.timestamps
end
Saya dapat menambahkan kolom cap waktu secara manual dengan yang berikut:
change_table :my_table do |t|
t.datetime :created_at, null: false, default: DateTime.now
t.datetime :updated_at, null: false, default: DateTime.now
end