Thêm dấu thời gian vào bảng hiện có


173

Tôi cần thêm dấu thời gian ( created_at& updated_at) vào bảng hiện có. Tôi đã thử mã sau đây nhưng nó không hoạt động.

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

Câu trả lời:


211

Trình trợ giúp dấu thời gian chỉ có sẵn trong create_tablekhối. Bạn có thể thêm các cột này bằng cách chỉ định các loại cột theo cách thủ công:

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

Mặc dù điều này không có cú pháp ngắn gọn như add_timestampsphương pháp bạn đã chỉ định ở trên, Rails vẫn sẽ coi các cột này là cột dấu thời gian và cập nhật các giá trị bình thường.


10
Điều này không làm việc cho tôi trong Rails 4. Giải pháp dưới đây của "mu quá ngắn" đang hoạt động.
newUserNameĐây là

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime- một phím tắt để tạo di chuyển ở trên.
Konstantine Kalbazov

2
Chạy di chuyển này dẫn đến lỗi PG::NotNullViolation: ERROR: column "created_at" contains null value vì bảng của tôi đã chứa dữ liệu vi phạm ràng buộc không null. Bất kỳ cách nào tốt hơn để làm điều này hơn là loại bỏ các tranh chấp không null lúc đầu và sau đó thêm nó sau?
M. Habib

1
@ M.Habib Tôi không nghĩ vậy, nhưng câu trả lời này gói gọn tất cả trong một lần di chuyển độc đáo.
littleforest

1
@ M.Habib phụ thuộc vào những gì bạn nghĩ có ý nghĩa nhất đối với giá trị mặc định, bạn có thể làm add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now. Time.zone.nowchỉ là một ví dụ, bạn nên sử dụng bất kỳ giá trị nào có ý nghĩa cho logic của bạn.
Delong Gao

91

Di chuyển chỉ là hai phương thức lớp (hoặc phương thức cá thể trong 3.1): updown(và đôi khi là changephương thức cá thể trong 3.1). Bạn muốn các thay đổi của bạn đi vào upphương thức:

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

Nếu bạn ở 3.1 thì bạn cũng có thể sử dụng change(cảm ơn Dave):

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

Có lẽ bạn đang bối rối def change, def change_tablechange_table.

Xem hướng dẫn di chuyển để biết thêm chi tiết.


1
(Chà, changebây giờ có phương pháp, mặc dù trong trường hợp này, không phải là vấn đề :)
Dave Newton

@Dave: Đúng vậy, tôi đã đi chung chung để tránh các vấn đề về phiên bản nhưng changeđáng được đề cập vì vậy tôi cũng sẽ thêm điều đó.
mu quá ngắn

Đúng là tôi nhưng tôi đã nghe nói rằng điều đó thực sự thay đổi với 3.1 và 'xuống' thực sự sẽ biến mất. Rails để tìm ra phương pháp xuống tự động. Bạn đã nghe nói về điều đó?
Michael Durrant

@Michael: Tôi đã sử dụng MongoDB độc quyền với ứng dụng 3.1 Tôi đang làm việc vì vậy tôi đã không làm việc với các lần di chuyển 3.1 AR. Các tài liệu chỉ ra rằng mọi thứ đang chuyển sang các phương thức cá thể (không rõ lý do).
mu quá ngắn

@MichaelDurrant, có nhiều kịch bản "thay đổi" không bao gồm ngay bây giờ, nếu lên / xuống sẽ có một số người tức giận :) (thêm một mệnh đề "trừ khi" trong di chuyển thay đổi của bạn để tránh va chạm di chuyển và thử quay trở lại ...) Thậm chí 3 năm sau khi bạn đưa ra nhận xét này, tôi không nghĩ nó đang thay đổi. :)
frandroid

76

Mã ban đầu của bạn rất gần bên phải, bạn chỉ cần sử dụng một tên phương thức khác. Nếu bạn đang sử dụng Rails 3.1 trở lên, bạn cần xác định changephương thức thay vì change_table:

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

Nếu bạn đang sử dụng phiên bản cũ hơn, bạn cần xác định updownphương pháp thay vì change_table:

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

  def down
    remove_timestamps(:users)
  end
end

58

Phản hồi của @ user1899434 đã nhận ra rằng một bảng "hiện có" ở đây có thể có nghĩa là một bảng có các bản ghi đã có trong đó, các bản ghi mà bạn có thể không muốn bỏ. Vì vậy, khi bạn thêm dấu thời gian bằng null: false, là mặc định và thường được mong muốn, những bản ghi hiện có đều không hợp lệ.

Nhưng tôi nghĩ rằng câu trả lời có thể được cải thiện, bằng cách kết hợp hai bước thành một lần di chuyển, cũng như sử dụng phương thức add_timestamp có ngữ nghĩa hơn:

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

Bạn có thể thay thế một số dấu thời gian khác cho DateTime.now, như nếu bạn muốn các bản ghi có sẵn được tạo / cập nhật vào lúc bình minh thay thế.


2
Kinh ngạc. Cảm ơn bạn! Chỉ cần một lưu ý - Time.zone.nowlà những gì nên được sử dụng nếu chúng ta muốn mã của mình tuân theo múi giờ chính xác.
John Gallagher

4
Có một vấn đề với việc đặt mặc định Time.zone.nowlà nó sẽ trả về thể hiện Thời gian được tạo khi di chuyển được chạy và chỉ sử dụng thời gian đó làm mặc định. Các đối tượng mới sẽ không nhận được một ví dụ Thời gian mới.
Tovi Newman

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

Các biến đổi có sẵn là

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/groupes/ActiveRecord/ConnectionAd Chap /Table.html


10

Câu trả lời của Nick Davies là đầy đủ nhất về việc thêm các cột dấu thời gian vào một bảng có dữ liệu hiện có. Nhược điểm duy nhất của nó là nó sẽ tăng ActiveRecord::IrreversibleMigrationlên a db:rollback.

Nó nên được sửa đổi như vậy để làm việc theo cả hai hướng:

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

Điều này không hoạt động chính xác như được viết cho tôi trên Rails 4.2.7 (Tôi nghĩ rằng change_column_defaultkhông hỗ trợ fromtotrong phiên bản đó?), Nhưng tôi đã lấy ý tưởng này và tạo ra up/downcác phương thức thay vì một changephương thức duy nhất và nó hoạt động như một cơ duyên!
gar


4

không chắc chắn chính xác khi nào nó được giới thiệu, nhưng trong rails 5.2.1 bạn có thể làm điều này:

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

để biết thêm xem " sử dụng phương pháp thay đổi " trong tài liệu di chuyển bản ghi hoạt động.


Tôi đã không làm cho nó hoạt động với Migration [5.1]; sau đó tôi đã thay đổi số thành [5.2] và Rails nói với tôi rằng tôi chỉ có thể sử dụng 5.1, 5.0 hoặc 4.2. Tôi đã thử với 5.0 nhưng không thành công, sau đó là 4.2 với thành công.
Là Ma

Cũ, tôi biết, nhưng nếu bạn có hồ sơ hiện có, hãy thêm: , null: truesau:my_table
jomar

2

Tôi đã tạo một hàm đơn giản mà bạn có thể gọi để thêm vào mỗi bảng (giả sử bạn có cơ sở dữ liệu hiện có) các trường created_atupdate_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_timestamp (tên_bảng, tùy chọn = {}) công khai

Thêm các dấu thời gian (created_at và update_at) vào tên_bảng. Các tùy chọn bổ sung (như null: false) được chuyển tiếp đến #add_column.

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

1

Các câu trả lời trước đây có vẻ đúng tuy nhiên tôi gặp phải vấn đề nếu bảng của tôi đã có mục.

Tôi sẽ nhận được 'ERROR: cột created_atchứa nullcác giá trị'.

Để khắc phục, tôi đã sử dụng:

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

Sau đó, tôi đã sử dụng gem Mig_data để thêm thời gian cho các dự án hiện tại khi di chuyển, chẳng hạn như:

def data
  Project.update_all created_at: Time.now
end

Sau đó, tất cả các dự án được tạo sau khi di chuyển này sẽ được cập nhật chính xác. Đảm bảo rằng máy chủ cũng được khởi động lại để Rails ActiveRecordbắt đầu theo dõi dấu thời gian trên bản ghi.


1

Rất nhiều câu trả lời ở đây, nhưng tôi cũng sẽ đăng câu hỏi của mình vì không có câu trả lời nào trước đây thực sự hiệu quả với tôi :)

Như một số người đã lưu ý, #add_timestampskhông may thêm null: falsehạn chế, điều này sẽ khiến các hàng cũ không hợp lệ vì chúng không có các giá trị này. Hầu hết các câu trả lời ở đây cho thấy rằng chúng tôi đặt một số giá trị mặc định ( Time.zone.now), nhưng tôi không muốn làm điều đó bởi vì các dấu thời gian mặc định cho dữ liệu cũ này sẽ không chính xác. Tôi không thấy giá trị trong việc thêm dữ liệu không chính xác vào bảng.

Vì vậy, việc di chuyển của tôi chỉ đơn giản là:

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

Không null: false, không có hạn chế khác. Các hàng cũ sẽ tiếp tục hợp lệ với created_atas NULLupdate_atas NULL(cho đến khi một số cập nhật được thực hiện cho hàng). Hàng mới sẽ có created_atupdated_atdân cư như mong đợi.


1

Vấn đề với hầu hết các câu trả lời ở đây là nếu bạn mặc định cho Time.zone.nowtất cả các bản ghi sẽ có thời gian di chuyển được chạy như thời gian mặc định của chúng, đó có thể không phải là điều bạn muốn. Thay vào đó, bạn có thể sử dụng đường ray 5 now(). Điều này sẽ đặt dấu thời gian cho các bản ghi hiện tại là thời gian di chuyển được chạy và là thời gian bắt đầu của giao dịch cam kết cho các bản ghi mới được chèn.

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


1

Sử dụng Time.currentlà một phong cách tốt 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

hoặc là

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

Đây là một cách đơn giản để thêm dấu thời gian trong bảng hiện có.

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

0

Đối với những người không sử dụng Rails nhưng sử dụng Activerecord, phần sau đây cũng thêm một cột vào mô hình hiện có, ví dụ dành cho trường số nguyên.

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

0

Đó là change, không change_tabledành cho Rails 4.2:

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

0

Đây có vẻ như là một giải pháp sạch trong Rails 5.0.7 (đã phát hiện ra phương thức 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

Tôi đang trên đường ray 5.0 và không có tùy chọn nào trong số này hoạt động.

Điều duy nhất hoạt động là sử dụng loại là: dấu thời gian và không: datetime

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

-1

Cá nhân tôi đã sử dụng những điều sau đây và nó đã cập nhật tất cả các hồ sơ trước đó với thời gian / ngày hiện tại:

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

Tôi gặp vấn đề tương tự trên Rails 5 khi cố gắng sử dụng

change_table :my_table do |t|
    t.timestamps
end

Tôi đã có thể thêm các cột dấu thời gian theo cách thủ công bằng cách sau:

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

Điều này sẽ không luôn đặt giá trị mặc định theo thời gian tại thời điểm di chuyển được chạy? (vì vậy không thực sự là dấu thời gian động do DB xử lý)
Guillaume Petit

đối với các bản ghi đã tồn tại trong db của bạn, vâng, nó sẽ đặt created_at và update_at thành datetime mà di chuyển đã được chạy. Mặc dù không có các giá trị đó trước đó, idk bạn sẽ khởi tạo các giá trị đó như thế nào. EDIT: Nó sẽ chỉ được coi là khởi đầu của lịch sử của hàng đó
Andres Rosales
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.