วิธีตั้งค่าเริ่มต้นสำหรับคอลัมน์วันที่และเวลาเพื่อบันทึกเวลาสร้างในการย้ายข้อมูล


114

พิจารณาสคริปต์การสร้างตารางด้านล่าง:

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end

เป็นไปได้ไหมที่จะตั้งค่าเริ่มต้นเป็นเวลาปัจจุบัน

ฉันกำลังพยายามค้นหา DB อิสระเทียบเท่าในรางสำหรับคำจำกัดความคอลัมน์ SQL ที่ระบุด้านล่าง:

ไวยากรณ์ของ Oracle

start_at DATE DEFAULT SYSDATE() 

MySQL Syntax

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

หรือ

start_at DATETIME DEFAULT NOW()

คำตอบ:


174

ตอนนี้รองรับใน Rails 5 แล้ว

นี่คือตัวอย่างการย้ายข้อมูล:

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }
      t.timestamps
    end
  end 
end

ดูการอภิปรายที่https://github.com/rails/rails/issues/27077และตอบคำถามโดย prathamesh-sonpatki


14
คำตอบที่ดี นอกจากนี้โปรดทราบว่าใน Postgres CURRENT_TIMESTAMPจะเป็นเวลาเริ่มต้นของธุรกรรมปัจจุบันดังนั้นหลายระเบียนที่สร้างขึ้นในธุรกรรมเดียวกันจะได้รับค่าเดียวกัน หากคุณต้องการเวลาปัจจุบันที่เกิดขึ้นจริงรันคำสั่ง (ละเลยบริบทการทำธุรกรรม), CLOCK_TIMESTAMPตรวจสอบ
Abe Voelker

ฉันเพิ่มสิ่งนี้: add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }และนี่คือสิ่งที่ดูเหมือนใน schema.rb t.datetime "start_date", default: -> { "now()" }แต่เมื่อฉันสร้างระเบียนใหม่มันจะไม่ถูกเติม คิดว่าทำไม?
Sandip Subedi

@SandipSubedi เดียวกันสำหรับฉัน บนราง 5.2.3.
Courtsimas

1
@courtsimas คุณต้องทำpost.reloadเพื่อให้ได้ค่าเหล่านั้น มีคำอธิบายที่นี่: stackoverflow.com/questions/53804787/…
Sandip Subedi

1
@SandipSubedi ฉันรู้ว่า 5 นาทีหลังจากความคิดเห็นของฉัน เช่นเดียวกับการสร้าง uuid ขอบคุณ!
Courtsimas

119

คุณสามารถเพิ่มฟังก์ชันในโมเดลได้ดังนี้:

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end

เพื่อให้โมเดลตั้งเวลาปัจจุบันในโมเดล

คุณยังสามารถวางโค้ด sql ในการโอนย้ายเพื่อตั้งค่าเริ่มต้นที่ระดับฐานข้อมูลได้เช่น:

execute 'alter table foo alter column starts_at set default now()'

การตั้งค่าสิ่งนี้:

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end

ทำให้เรียกใช้ฟังก์ชัน Time.now ระหว่างการโอนย้ายดังนั้นตารางในฐานข้อมูลจึงถูกสร้างขึ้นดังนี้:

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');

แต่ฉันคิดว่านั่นไม่ใช่สิ่งที่คุณต้องการ


1
ตอนนี้ฉันกำลังตั้งค่าใน: before_create callback <br> ฉันกำลังมองหาเวทมนตร์ AR บางประเภทที่นี่ ฉันใช้เวลาดูรหัส Rails อยู่พักหนึ่ง แต่ไม่พบวิธีแก้ปัญหาใด ๆ ฉันคิดว่าฉันจะถามรอบ ๆ เพื่อดูว่ามีทางเลือกอื่นหรือไม่
Harish Shetty

ฉันขอแนะนำให้ทำด้วยการโทรกลับใน before_create
jonnii

1
ฉันไม่ต้องการเปลี่ยนแปลงตาราง DB เพราะฉันต้องการให้โค้ด DB เป็นกลาง ฉันหวังว่า AR จะมีกลไกบางอย่างในการตั้งค่าเริ่มต้นสำหรับฟิลด์ Datetime ที่คล้ายกับฟิลด์ created_at
Harish Shetty

9
โปรดทราบว่าbefore_create callback รันก่อนที่จะแทรกลงในฐานข้อมูล แต่หลังจากที่คุณสร้างอินสแตนซ์อ็อบเจ็กต์ของคุณแล้ว (เช่นเดียวกับnew()) ดังนั้นจะแทนที่ค่าที่คุณอาจจะให้ไปself.foo = Time.now new()ฉันขอแนะนำself.foo = Time.current unless self.foo.present?แทน
Fatih

2
ไม่ว่าเมื่อใช้ execute สิ่งนี้จะใส่บรรทัด:starts_at, :default => 'now()'ใน schema.rb อย่างไรก็ตามสิ่งนี้ใช้ไม่ได้เมื่อใช้rake db:schema:dumpซึ่งจะแทนที่ schema.rb ด้วย :starts_at, :default => '2015-05-29 09:46:33'(หรือวันใดก็ตามที่คุณเปิดสคริปต์) ... เศร้า ....
astreal

13

Active Record โดยอัตโนมัติ timestamps สร้างและปรับปรุงการดำเนินงานถ้าตารางมีเขตข้อมูลชื่อcreated_at/ created_onหรือ/updated_at ที่มา - api.rubyonrails.orgupdated_on

คุณไม่จำเป็นต้องทำสิ่งอื่นใดนอกจากมีคอลัมน์นั้น


ฉันมีช่องเหล่านั้นในตารางแล้ว ฉันต้องการฟิลด์เพิ่มเติมสำหรับตัวกำหนดตารางเวลาของฉันเพื่อเก็บวันที่เริ่มต้น ขณะนี้ฉันใช้: before_create callback เพื่อกำหนดวันที่ปัจจุบัน หากฉันพบสถานการณ์นี้บ่อยๆฉันต้องใช้การเขียนปลั๊กอินเพื่อแก้ไขการจัดการค่าเริ่มต้นในเมธอด 'to_sql' ของคลาส ColumnDefinition
Harish Shetty

10

ฉันมักจะทำ:

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end

ดังนั้นคุณschema.rbจะมีสิ่งที่ต้องการ:

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end

ข้อเสีย: การแก้ปัญหานี้จะทำงานเฉพาะเมื่อคุณเรียกใช้ไม่ได้เมื่อคุณโหลดไฟล์สคีของคุณกับสิ่งที่ต้องการrake db:migrate rake db:schema:load
Alter Lagos

9

ฉันกำลังมองหาโซลูชั่นที่คล้ายกัน แต่ฉันสิ้นสุดการใช้https://github.com/FooBarWidget/default_value_for

default_value_forปลั๊กอินช่วยให้หนึ่งในการกำหนดค่าเริ่มต้นสำหรับรุ่น ActiveRecord ในลักษณะที่เปิดเผย ตัวอย่างเช่น:

class User < ActiveRecord::Base
  default_value_for :name, "(no name)"
  default_value_for :last_seen do
    Time.now
  end
end

u = User.new
u.name       # => "(no name)"
u.last_seen  # => Mon Sep 22 17:28:38 +0200 2008

8

หากคุณจำเป็นต้องเปลี่ยนคอลัมน์ DateTime ที่มีอยู่ในรางที่ 5 (มากกว่าการสร้างตารางใหม่ที่ระบุไว้ในคำตอบอื่น ๆ ) เพื่อให้สามารถใช้ประโยชน์จากความสามารถในการเริ่มต้นวันที่คุณสามารถสร้างการโยกย้ายเช่นนี้:

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end

แบบฟอร์มที่ไม่ดีในการลงคะแนนบางสิ่งบางอย่างลงและไม่ได้อธิบายว่ามีปัญหาอะไรกับคำตอบ ใครมีความคิดว่าทำไมสิ่งนี้จึงถูกโหวตลง? จะแสดงไวยากรณ์หากคุณต้องการเปลี่ยนคอลัมน์แทนการสร้างใหม่
Matt Long

ฉันไม่ใช่คนที่ลงคะแนน แต่ฉันมีปัญหาในการดูว่าคำตอบนี้แตกต่างจากคำตอบของพินัยกรรมที่เกิดขึ้นก่อนหน้าคุณภายในหนึ่งปีอย่างไร คำถามเกี่ยวกับการตั้งค่าเริ่มต้นและคำตอบของคุณมีส่วนคำสั่ง lambda เหมือนกัน
nurettin

2
@nurettin ฉันเข้าใจคุณ แต่ในการป้องกันของฉันไวยากรณ์สำหรับการสร้างคอลัมน์ใหม่นั้นแตกต่างจากการเปลี่ยนคอลัมน์ที่มีอยู่ การให้ไวยากรณ์นั้นสำหรับผู้ที่พบคำถามนี้ผ่านการค้นหาเว็บในขณะที่พยายามเพิ่มค่าเริ่มต้นให้กับโมเดลข้อมูลปัจจุบันแทนที่จะสร้างโมเดล / ตารางใหม่ทั้งหมดน่าจะมีประโยชน์มาก ไม่มี? คุณสมมติว่าทุกคนรู้ว่าพวกเขาสามารถใช้ lambda เดียวกันสำหรับ change_column ได้ บางทีพวกเขาควรจะรู้ แต่นั่นคือเหตุผลที่ฉันตอบที่นี่ - ดังนั้นพวกเขาจึงไม่ต้องไปหาที่อื่นเพื่อหาคำตอบ ไชโย!
Matt Long

4
FWIW ตอนนี้ฉันเพิ่งใช้ไวยากรณ์นี้และขอขอบคุณสำหรับการค้นหาโดย Google ของฉันและปัญหาเฉพาะคำตอบนี้ช่วยฉันได้มากที่สุด
Jay Killeen

1
ฉันไม่เห็นอะไรผิดปกติกับการเป็นคำตอบมากกว่าการแสดงความคิดเห็น upvoted ฉันคิดว่าผู้ลงคะแนนกำลังอ่านอย่างไม่ใส่ใจ (เช่นเดียวกับผู้ปิดคำถามส่วนใหญ่!; p)
iconoclast

-1

ในคำตอบที่ได้รับจาก @ szymon-lipiński (Szymon Lipiński) วิธีดำเนินการไม่ได้ผลสำหรับฉัน มันทำให้เกิดข้อผิดพลาดทางไวยากรณ์ MySQL

ไวยากรณ์ MySQL ที่ใช้ได้ผลสำหรับฉันคือสิ่งนี้

execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"

ดังนั้นในการตั้งค่าเริ่มต้นสำหรับคอลัมน์วันที่และเวลาในสคริปต์การโอนย้ายสามารถทำได้ดังนี้:

def up
  create_table :foo do |t|
    t.datetime :starts_at, :null => false
  end

  execute "ALTER TABLE `foo` CHANGE `starts_at` `starts_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.