移行の作成時間を記録するために日時列のデフォルト値を設定するにはどうすればよいですか?


114

以下のテーブル作成スクリプトを検討してください。

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

デフォルト値を現在の時間として設定することは可能ですか?

以下に示すSQL列定義のレールでDBに依存しない同等のものを見つけようとしています:

Oracle構文

start_at DATE DEFAULT SYSDATE() 

MySQL構文

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。
コートシマ

1
post.reloadこれらの値を取得するために必要な@courtsimas 。それは、ここで説明するの:stackoverflow.com/questions/53804787/...
Sandipスベディ

1
@SandipSubedi私は私のコメントの5分後に気づきました。ちょうどuuid生成と同じです。ありがとう!
コートシマ

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コールバックで値を設定しています。<br>ここで何らかのタイプのARマジックを探していました。Railsのコードを確認するのに少し時間をかけましたが、解決策は見つかりませんでした。他に選択肢があるかどうか尋ねてみようと思いました。
Harish Shetty、

before_createのコールバックでそれを行うことをお勧めします。
jonnii 2009年

1
コードDBを中立に保ちたいので、DBテーブルを変更したくありません。ARには、created_atフィールドと同様に、Datetimeフィールドのデフォルト値を設定するための何らかのメカニズムがあることを望んでいました。
Harish Shetty、

9
before_createコールバックは、データベースに挿入する前に、オブジェクトをインスタンス化した後(などnew())に実行されることに注意してください。だからself.foo = Time.nowあなたが与えるかもしれない値を上書きしますnew()self.foo = Time.current unless self.foo.present?代わりに提案します。
Fatih、2014

2
実行を使用する:starts_at, :default => 'now()'と、schema.rb に1行挿入されます。ただしrake db:schema:dump、schema.rbを上書きする:starts_at, :default => '2015-05-29 09:46:33'(またはスクリプトを起動した日付は何でも)を使用すると 、これは機能しません...悲しい...
astreal

13

テーブルにcreated_at/ created_onまたはupdated_at/ という名前のフィールドがある場合、アクティブレコードは自動的にタイムスタンプを作成および更新しますupdated_onソース-api.rubyonrails.org

その列がある以外は何もする必要はありません。


テーブルにそれらのフィールドがすでにあります。スケジューラーが開始日を保持するための追加フィールドが必要です。現在、私は:before_createコールバックを使用して現在の日付を設定しています。このシナリオが頻繁に発生する場合は、ColumnDefinitionクラスの「to_sql」メソッドのデフォルト値の処理を変更するプラグインを作成する必要があります。
Harish Shetty、

9

私は同様のソリューションを探していましたが、https://github.com/FooBarWidget/default_value_forの使用を終了しました。

default_value_forこのプラグインは1つが、宣言的に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

9

私は通常します:

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

8

Rails 5で既存のDateTime列変更する必要がある場合(他の回答で指定されているように新しいテーブルを作成するのではなく)、デフォルトの日付機能を利用できるようにするには、次のような移行を作成できます。

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

何かに反対票を投じ、回答でどの問題が取り上げられたかを説明しないのは不適切な形式です。なぜこれが否決されたのか誰かが知っていますか?列を作成するのではなく変更したい場合は、構文が表示されます。
マットロング

私は反対票を投じたのではありませんが、この回答があなたの回答より1年前の意志の回答とどのように異なるのかを理解するのに苦労しています。問題はデフォルトを設定することであり、あなたの答えは同じラムダ節を持っています。
ヌレティン2018年

2
@nurettin要点は分かりますが、私の弁護では、新しい列を作成するための構文は、既存の列を変更することとは微妙に異なります。新しいモデル/テーブルを完全に作成するのではなく、現在のデータモデルにデフォルトを追加しようとしているときに、ウェブ検索でこの質問を見つけた人にその構文を提供することは、おそらくかなり役に立ちます。番号?change_columnに同じラムダを使用できることを誰もが知っていると想定しています。多分彼らはそれを理解するべきですが、それが私がここで答えた理由です-それで彼らはそれを理解するために他のどこかに行く必要はありません。乾杯!
Matt Long

4
FWIW私は今この構文を使用しており、グーグル検索と特定の問題でこの回答が最も役に立ちました。
ジェイキリーン2018年

1
これがコメントではなく回答であることには何の問題もありません。賛成。私は、反対投票者が不注意に読んでいるだけだと思います(ほとんどの質問クローザーのように!; p)。
iconoclast

-1

@szymon-lipiński(SzymonLipiński)の回答では、executeメソッドが機能しませんでした。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.