เพิ่มการประทับเวลาลงในตารางที่มีอยู่


173

ฉันต้องการเพิ่มการประทับเวลา ( created_at& updated_at) ลงในตารางที่มีอยู่ ฉันลองใช้รหัสต่อไปนี้ แต่มันไม่ทำงาน

class AddTimestampsToUser < ActiveRecord::Migration
    def change_table
        add_timestamps(:users)
    end
end

คำตอบ:


211

ตัวช่วยประทับเวลาจะมีเฉพาะในcreate_tableบล็อกเท่านั้น คุณสามารถเพิ่มคอลัมน์เหล่านี้โดยการระบุประเภทคอลัมน์ด้วยตนเอง:

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

แม้ว่าสิ่งนี้จะไม่ได้มีรูปแบบของคำศัพท์แบบเดียวกับที่add_timestampsคุณได้ระบุไว้ข้างต้น แต่ Rails จะยังคงปฏิบัติต่อคอลัมน์เหล่านี้เป็นคอลัมน์ประทับเวลาและอัปเดตค่าตามปกติ


10
สิ่งนี้ไม่ได้ผลสำหรับฉันใน Rails 4 การแก้ปัญหาด้านล่างโดย "mu สั้นเกินไป" ใช้งานได้
นี่

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime- ทางลัดเพื่อสร้างการย้ายข้อมูลข้างต้น
Konstantine Kalbazov

2
การเรียกใช้การย้ายข้อมูลนี้ทำให้เกิดข้อผิดพลาด PG::NotNullViolation: ERROR: column "created_at" contains null value เนื่องจากตารางของฉันมีข้อมูลที่ละเมิดข้อ จำกัด ที่ไม่มีค่าว่างอยู่แล้ว มีวิธีใดที่ดีกว่าในการทำเช่นนี้แทนที่จะลบข้อ จำกัด ที่ไม่ใช่โมฆะในตอนแรกจากนั้นจึงเพิ่มในภายหลัง
M. Habib

1
@ M.Habib ฉันไม่คิดอย่างนั้น แต่คำตอบนี้จะรวมเอาไว้ในการโยกย้ายครั้งเดียว
littleforest

1
@ M.Habib add_column :users, :updated_at, :datetime, null: false, default: Time.zone.nowขึ้นอยู่กับสิ่งที่คุณคิดว่าเหมาะสมที่สุดสำหรับค่าเริ่มต้นที่คุณสามารถทำได้ Time.zone.nowเป็นเพียงตัวอย่างคุณควรใช้ค่าใด ๆ ที่สมเหตุสมผลกับตรรกะของคุณ
Delong Gao

91

การย้ายข้อมูลเป็นเพียงวิธีการเรียนสองวิธี (หรือวิธีการตัวอย่างใน 3.1): upและdown(และบางครั้งเป็นchangeวิธีการตัวอย่างใน 3.1) คุณต้องการให้การเปลี่ยนแปลงของคุณเป็นไปตามupวิธีการ:

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

หากคุณอยู่ใน 3.1 คุณสามารถใช้change(ขอบคุณเดฟ):

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table(:users) { |t| t.timestamps }
  end
end

บางทีคุณอาจกำลังสับสนdef change, และdef change_tablechange_table

ดูคู่มือการโยกย้ายสำหรับรายละเอียดเพิ่มเติม


1
(ดีมีเป็นchangeวิธีการในขณะนี้แม้ว่าในกรณีนี้ไม่เป็นปัญหา :)
เดฟนิวตัน

@Dave: จริงพอฉันไปทั่วไปเพื่อหลีกเลี่ยงปัญหารุ่น แต่changeมีมูลค่าการกล่าวถึงดังนั้นฉันจะเพิ่มที่เกินไป
mu สั้นเกินไป

True mu แต่ฉันได้ยินมาว่ามันเปลี่ยนไปจริง ๆ ด้วย 3.1 และ 'down' กำลังจะหายไป Rails เพื่อหาวิธีการลงโดยอัตโนมัติ คุณเคยได้ยินเรื่องนี้บ้างไหม?
Michael Durrant

@Michael: ฉันใช้ MongoDB เฉพาะกับแอป 3.1 ที่ฉันใช้งานอยู่ดังนั้นฉันจึงไม่ได้ทำงานกับการโยกย้าย 3.1 AR เอกสารระบุว่าทุกอย่างกำลังเคลื่อนไปสู่วิธีการแบบอินสแตนซ์
mu สั้นเกินไป

@MichaelDurrant มีหลายสถานการณ์ที่ "การเปลี่ยนแปลง" ไม่ครอบคลุมในขณะนี้หากขึ้น / ลงไปจะมีคนโกรธ :) (เพิ่มส่วน "ยกเว้น" ในการย้ายข้อมูลการเปลี่ยนแปลงของคุณเพื่อหลีกเลี่ยงการชนกันของการย้ายถิ่นและลอง ย้อนกลับไปที่ ... ) แม้กระทั่ง 3 ปีหลังจากที่คุณแสดงความคิดเห็นนี้ฉันไม่คิดว่ามันจะเปลี่ยนไป :)
frandroid

76

รหัสต้นฉบับของคุณใกล้ด้านขวาคุณเพียงแค่ต้องใช้ชื่อวิธีอื่น หากคุณใช้ Rails 3.1 หรือใหม่กว่าคุณจะต้องกำหนดchangeวิธีการแทนchange_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

หากคุณใช้เวอร์ชั่นที่เก่ากว่าคุณต้องกำหนดupและdownวิธีการแทนchange_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def up
    add_timestamps(:users)
  end

  def down
    remove_timestamps(:users)
  end
end

59

การตอบสนองของ @ user1899434 เกิดขึ้นจากข้อเท็จจริงที่ว่าตาราง "ที่มีอยู่" ที่นี่อาจหมายถึงตารางที่มีระเบียนอยู่ในนั้นแล้วบันทึกที่คุณอาจไม่ต้องการวาง ดังนั้นเมื่อคุณเพิ่มการประทับเวลาด้วย null: false ซึ่งเป็นค่าเริ่มต้นและมักเป็นที่ต้องการบันทึกที่มีอยู่เหล่านั้นจะไม่ถูกต้องทั้งหมด

แต่ฉันคิดว่าคำตอบนั้นสามารถปรับปรุงได้โดยการรวมสองขั้นตอนเข้าด้วยกันในการโยกย้ายครั้งเดียวรวมทั้งใช้เมธอด add_timestamps ที่มีความหมายมากขึ้น:

def change
  add_timestamps :projects, default: Time.zone.now
  change_column_default :projects, :created_at, nil
  change_column_default :projects, :updated_at, nil
end

คุณสามารถแทนที่การประทับเวลาอื่น ๆDateTime.nowได้เช่นถ้าคุณต้องการให้มีการสร้าง / อัปเดตระเบียนที่มีมาก่อนในตอนเช้าแทน


2
น่าอัศจรรย์ ขอบคุณ! เพียงหนึ่งบันทึก - Time.zone.nowเป็นสิ่งที่ควรใช้ถ้าเราต้องการให้รหัสของเราเป็นไปตามเขตเวลาที่ถูกต้อง
John Gallagher

4
มีปัญหาในการตั้งค่าเริ่มต้นTime.zone.nowซึ่งจะส่งคืนอินสแตนซ์เวลาที่สร้างขึ้นเมื่อมีการเรียกใช้การย้ายข้อมูลและใช้เวลานั้นเป็นค่าเริ่มต้น วัตถุใหม่จะไม่ได้รับอินสแตนซ์เวลาใหม่
Tovi Newman

38
class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table :users do |t|
      t.timestamps
    end
  end
end

การเปลี่ยนแปลงที่มีอยู่คือ

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


10

คำตอบของ Nick Daviesนั้นสมบูรณ์ที่สุดในแง่ของการเพิ่มคอลัมน์ประทับเวลาลงในตารางที่มีข้อมูลที่มีอยู่ เสียเพียงอย่างเดียวของมันคือมันจะขึ้นอยู่กับActiveRecord::IrreversibleMigrationdb:rollback

มันควรได้รับการปรับเปลี่ยนเพื่อให้ทำงานได้ทั้งสองทิศทาง:

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

สิ่งนี้ใช้งานไม่ได้เหมือนที่เขียนไว้สำหรับฉันบน Rails 4.2.7 (ฉันคิดว่าchange_column_defaultไม่รองรับfromและtoในเวอร์ชั่นนั้น) แต่ฉันใช้ความคิดนี้และสร้างup/downวิธีการแทนวิธีการเดียวchangeและใช้งานได้ดี!
gar


4

ไม่แน่ใจว่าสิ่งนี้ถูกนำมาใช้อย่างแน่นอน แต่ในทางรถไฟ 5.2.1 คุณสามารถทำได้:

class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
  def change
    add_timestamps :my_table
  end
end

เพื่อดูเพิ่มเติม " ใช้วิธีการเปลี่ยนแปลง " ในเอกสารการโยกย้ายบันทึกที่ใช้งานอยู่


ฉันไม่ได้ทำงานให้กับ Migration [5.1]; จากนั้นฉันเปลี่ยนหมายเลขเป็น [5.2] และ Rails บอกฉันว่าฉันสามารถใช้ 5.1, 5.0 หรือ 4.2 ได้เท่านั้น ฉันลองกับ 5.0 โดยไม่ประสบความสำเร็จแล้วใน 4.2 กับความสำเร็จ
คือ Ma

เก่าฉันรู้ แต่ถ้าคุณมีบันทึกที่มีอยู่เพิ่ม: , null: trueหลังจาก:my_table
jomar

2

ฉันได้สร้างฟังก์ชั่นง่าย ๆ ที่คุณสามารถเรียกเพื่อเพิ่มไปยังแต่ละตาราง (สมมติว่าคุณมีฐานข้อมูลที่มีอยู่) เขตข้อมูลcreated_atและ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

2

add_timestamps (table_name, options = {}) พับลิก

เพิ่มคอลัมน์การประทับเวลา (created_at และ updated_at) ลงใน table_name ตัวเลือกเพิ่มเติม (เช่น null: false) จะถูกส่งต่อไปยัง #add_column

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users, null: false)
  end
end

1

คำตอบก่อนที่จะดูเหมือนจะถูก แต่ฉันประสบปัญหาหากตารางของฉันมีรายการแล้ว

ฉันจะได้รับ 'ข้อผิดพลาด: คอลัมน์created_atมีnullค่า'

เพื่อแก้ไขฉันใช้:

def up
  add_column :projects, :created_at, :datetime, default: nil, null: false
  add_column :projects, :updated_at, :datetime, default: nil, null: false
end

จากนั้นฉันใช้ gem migration_dataเพื่อเพิ่มเวลาสำหรับโครงการปัจจุบันในการย้ายข้อมูลเช่น:

def data
  Project.update_all created_at: Time.now
end

จากนั้นโครงการทั้งหมดที่สร้างหลังจากการโยกย้ายนี้จะได้รับการปรับปรุงอย่างถูกต้อง ตรวจสอบให้แน่ใจว่ามีการรีสตาร์ทเซิร์ฟเวอร์ด้วยเพื่อให้ Rails ActiveRecordเริ่มติดตามการประทับเวลาในบันทึก


1

มีคำตอบมากมายที่นี่ แต่ฉันจะโพสต์ของฉันด้วยเพราะไม่มีคำตอบก่อนหน้านี้ที่เหมาะกับฉันจริงๆ :)

ตามที่บางคนระบุไว้#add_timestampsแต่น่าเสียดายที่เพิ่มnull: falseข้อ จำกัด ซึ่งจะทำให้แถวเก่าไม่ถูกต้องเนื่องจากไม่มีค่าเหล่านี้ คำตอบส่วนใหญ่ที่นี่แนะนำว่าเราตั้งค่าเริ่มต้นบางอย่าง ( Time.zone.now) แต่ฉันไม่ต้องการทำเช่นนั้นเพราะการประทับเวลาเริ่มต้นสำหรับข้อมูลเก่าจะไม่ถูกต้อง ฉันไม่เห็นค่าในการเพิ่มข้อมูลที่ไม่ถูกต้องลงในตาราง

ดังนั้นการย้ายถิ่นของฉันเป็นเพียง:

class AddTimestampsToUser < ActiveRecord::Migration
  def change_table
    add_column :projects, :created_at, :datetime
    add_column :projects, :updated_at, :datetime
  end
end

ไม่null: falseไม่มีข้อ จำกัด อื่น ๆ แถวเก่าจะยังคงเป็นที่ถูกต้องcreated_atเป็นNULLและupdate_atเป็นNULL(จนถึงการปรับปรุงบางส่วนจะดำเนินการเพื่อแถว) แถวใหม่จะมีcreated_atและupdated_atเติมตามที่คาดไว้


1

ปัญหาของคำตอบส่วนใหญ่ที่นี่คือถ้าคุณเริ่มต้นTime.zone.nowระเบียนทั้งหมดจะมีเวลาที่การโยกย้ายถูกเรียกใช้เป็นเวลาเริ่มต้นซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ ในราง 5 now()คุณสามารถใช้แทน สิ่งนี้จะตั้งค่าการประทับเวลาสำหรับบันทึกที่มีอยู่เป็นเวลาที่การโยกย้ายถูกเรียกใช้และเป็นเวลาเริ่มต้นของการทำธุรกรรมการกระทำสำหรับการแทรกระเบียนใหม่

class AddTimestampsToUsers < ActiveRecord::Migration def change add_timestamps :users, default: -> { 'now()' }, null: false end end


1

การใช้Time.currentเป็นรูปแบบที่ดี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

หรือ

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

1

นี่เป็นวิธีง่ายๆในการเพิ่มการประทับเวลาในตารางที่มีอยู่

class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
  def change
    add_timestamps :custom_field_metadata
  end
end

0

สำหรับผู้ที่ไม่ได้ใช้ Rails แต่ใช้ activerecord ต่อไปนี้จะเพิ่มคอลัมน์ในโมเดลที่มีอยู่ตัวอย่างเช่นสำหรับฟิลด์จำนวนเต็ม

ActiveRecord::Schema.define do
  change_table 'MYTABLE' do |table|
    add_column(:mytable, :my_field_name, :integer)
  end
end

0

มันchangeไม่ใช่change_tableสำหรับ Rails 4.2:

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

0

ดูเหมือนว่าวิธีแก้ปัญหาแบบสะอาดใน Rails 5.0.7 (ค้นพบเมธอด 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

0

ฉันอยู่บนราง 5.0 และตัวเลือกเหล่านี้ไม่ทำงาน

สิ่งเดียวที่ทำงานได้คือใช้ชนิดเป็น: timestamp และไม่ใช่: datetime

def change
    add_column :users, :created_at, :timestamp
    add_column :users, :updated_at, :timestamp
end

-1

ฉันใช้สิ่งต่อไปนี้เป็นการส่วนตัวและปรับปรุงระเบียนก่อนหน้าทั้งหมดด้วยเวลา / วันที่ปัจจุบัน:

add_column :<table>, :created_at, :datetime, default: Time.zone.now, null: false
add_column :<table>, :updated_at, :datetime, default: Time.zone.now, null: false

-2

ฉันพบปัญหาเดียวกันกับ Rails 5 ที่พยายามใช้

change_table :my_table do |t|
    t.timestamps
end

ฉันสามารถเพิ่มคอลัมน์เวลาด้วยตนเองโดยทำสิ่งต่อไปนี้:

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

สิ่งนี้จะไม่ตั้งค่าเริ่มต้นเสมอพร้อมกับเวลาที่การโยกย้ายถูกเรียกใช้หรือไม่ (ดังนั้นไม่ใช่การประทับเวลาแบบไดนามิกที่จัดการโดย DB)
Guillaume Petit

สำหรับเร็กคอร์ดที่มีอยู่แล้วใน db ของคุณใช่แล้วมันจะตั้งค่า created_at และ updated_at เป็น datetime ที่การโอนย้ายถูกรัน โดยไม่ต้องมีค่าเหล่านั้นไว้ล่วงหน้า idk วิธีอื่นคุณจะเริ่มต้นค่าเหล่านั้น แก้ไข: มันจะถือเป็นจุดเริ่มต้นของประวัติศาสตร์ของแถวนั้น
Andres Rosales
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.