既存のテーブルにタイムスタンプを追加する


173

既存のテーブルにタイムスタンプ(created_atupdated_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 is too short」による以下の解決策が機能しています。
newUserNameHere 14

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime-上記の移行を生成するショートカット。
コンスタンティンカルバゾフ2014

2
この移行を実行するPG::NotNullViolation: ERROR: column "created_at" contains null value と、テーブルにnull以外の制約に違反するデータが既に含まれているため、エラーが発生し ます。最初にnullでない制約を削除し、後で追加するよりも、これを行うより良い方法はありますか?
M.ハビブ

1
@ M.Habib私はそうは思いませんが、この答えはすべてを1つの移行にうまくカプセル化します。
littleforest

1
@ M.Habibは、デフォルト値に対して最も理にかなっていると思うことに依存しますadd_column :users, :updated_at, :datetime, null: false, default: Time.zone.nowTime.zone.nowは単なる例であり、ロジックに適した値を使用する必要があります。
Delong Gao

91

マイグレーションは、2つのクラスメソッド(または3.1のインスタンスメソッド)だけです:(upおよび、3.1のインスタンスメソッドのdown場合もありchangeます)変更を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(Daveに感謝)も使用できます。

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

おそらく、あなたは混乱しているdef changedef change_tablechange_table

詳細については、移行ガイドを参照してください。


1
(まあ、ここではchange方法はありますが、この場合は問題ではありません:)
Dave Newton

@デーブ:確かに、バージョンの問題を回避するためにジェネリックを選びましたchangeが、言及する価値があるので、それも追加します。
muが短すぎる

真のミューですが、3.1でそれは本当に変わり、「ダウン」は本当になくなると聞いています。Railsでdownメソッドを自動的に把握します。聞いたことありますか?
マイケルデュラント

@Michael:自分が取り組んでいる3.1アプリでMongoDBを排他的に使用しているため、3.1 ARの移行を行っていません。ドキュメントは、すべてがインスタンスメソッドに向かっていることを示しています(理由は不明です)。
muが短すぎる

@MichaelDurrant、「変更」が現在カバーしていない多くのシナリオがあります。アップ/ダウンがなくなると、怒っている人がいます:)(移行の衝突を回避するために、変更の移行に「unless」句を追加して、ロールバックします...)このコメントを書いてから3年経っても、変化しているとは思いません。:)
frandroid

76

元のコードは非常に近く、別のメソッド名を使用する必要があるだけです。Rails 3.1以降を使用している場合は、次のchange代わりにメソッドを定義する必要がありますchange_table

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

古いバージョンを使用している場合は、代わりにupdownメソッドを定義する必要がありますchange_table

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

  def down
    remove_timestamps(:users)
  end
end

58

@ user1899434の応答は、ここにある「既存の」テーブルが、すでにレコードが含まれているテーブル、つまり削除したくないレコードである可能性があるという事実に基づいています。したがって、null:false(デフォルトであり、多くの場合望ましい)でタイムスタンプを追加すると、それらの既存のレコードはすべて無効になります。

しかし、2つのステップを1つの移行に結合し、よりセマンティックな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
すごい。ありがとうございました!たった1つのメモ- Time.zone.nowコードが正しいタイムゾーンに準拠するようにしたい場合に使用するものです。
ジョンギャラガー、

4
デフォルトの設定に問題があります。Time.zone.nowこれは、移行の実行時に作成されたTimeインスタンスを返し、その時間をデフォルトとして使用することです。新しいオブジェクトは新しいTimeインスタンスを取得しません。
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サポートしていないfromto?そのバージョンで)、私はこのアイデアを取り、作成したup/downメソッドを代わりに単一のchange方法、それが魅力のように働きました!
ガー


4

これが正確にいつ導入されたかはわかりませんが、rails 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では成功しました。
である馬

古い、私は知っているが、既存のレコードを持っている場合は追加:, 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 = {})public

タイムスタンプ(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_atnull値が含まれています」と表示されます。

修正するには、次を使用しました:

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_atas NULLupdate_atasを使用して引き続き有効になりますNULL(行に対して更新が実行されるまで)。新しい行がありますcreated_atし、updated_at期待通りに取り込ま。


1

ここでのほとんどの回答の問題は、Time.zone.nowすべてのレコードをデフォルトにすると、移行が実行された時刻がデフォルトの時刻になることです。これは、おそらく望ましいことではありません。Rails 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

私はRails 5.0を使用していますが、これらのオプションはどれも機能しませんでした。

機能したのは、型を:datetimeではなく:timestampにすることだけでした。

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

データベースに既に存在するレコードの場合、はい、created_atとupdated_atは、移行が実行された日時に設定されます。ただし、これらの値を事前に設定しておかなくても、他にどのようにしてこれらの値を初期化するかをidkします。編集:それはその行の歴史の始まりと見なされます
Andres Rosales
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.