web-dev-qa-db-ja.com

Railsタイムスタンプフィールドを自動的に更新しないようにする方法はありますか?

DB列がある場合created_atおよびupdated_at Railsは、モデルオブジェクトを作成および更新するときに、これらの値を自動的に設定します。これらの列に触れずにモデルを保存する方法はありますか?

いくつかのレガシーデータを取り込んでいますが、(異なる名前の)レガシーデータフィールドの対応する値からそれらの値を設定したいと思います。モデルにそれらを設定してからモデルを保存すると、Railsが入力値をオーバーライドするように見えます。

もちろん、それを防ぐためにRailsモデル列に別の名前を付けることができますが、データをインポートした後、自動タイムスタンプ処理を行うためにRailsが必要です。

66
Ethan

移行またはrakeタスク(またはEdge Railsを使用している場合は 新しいデータベースシード )でこれを実行します。

ActiveRecord::Base.record_timestamps = false
begin
  run_the_code_that_imports_the_data
ensure
  ActiveRecord::Base.record_timestamps = true  # don't forget to enable it again!
end

created_atupdated_atを手動で安全に設定できます。Railsは文句を言いません。

注:これは個々のモデルでも機能します。 User.record_timestamps = false

73
August Lilleaas

代わりにupdate_columnメソッドを使用します。

http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column

update_column(name, value)
# Updates a single attribute of an object, without calling save.

検証はスキップされます。

コールバックはスキップされます。

updated_at/updated_on列は、その列が使用可能な場合は更新されません。

新しいオブジェクトで呼び出されたとき、またはname属性が読み取り専用としてマークされたときにActiveRecordErrorを送出します。

55
yawl

Rails 5は、タイムスタンプを更新せずにレコードを更新する便利な方法を提供しますupdated_athttps://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save

レコードの更新中に、touch:falseを渡す必要があります。

>> user = User.first
>> user.updated_at
=> Thu, 28 Apr 2016 20:01:57 IST +05:30
>> user.name = "Jose"
>> user.save(touch: false)
=> true

>> user.updated_at
=> Thu, 28 Apr 2016 20:01:57 IST +05:30
36
Subhash Chandra

移行内で次を設定できます。

ActiveRecord::Base.record_timestamps = false

または、代わりにupdate_allを使用します。

pdate_all(更新、条件= nil、オプション= {})

指定された条件のセットに一致する場合、指定された詳細ですべてのレコードを更新します。制限と順序も指定できます。このメソッドは、単一のSQL UPDATEステートメントを構築し、データベースに直接送信します。関連するモデルをインスタンス化せず、Active Recordコールバックをトリガーしません。

30
Toby Hede

Rails 3+、単一のオブジェクトの場合、record_timestampsクラスではなくオブジェクト。つまり、

>> user = User.first
>> user.updated_at
=> Tue, 12 Apr 2016 22:47:51 GMT +00:00
>> user.name = "Jose"
=> "Jose"
>> user.record_timestamps = false
>> user.save
=> true
>> user.updated_at
=> Tue, 12 Apr 2016 22:47:51 GMT +00:00
>> User.record_timestamps
=> true

この方法では、モデルのグローバル状態に触れず、ensureブロックの以前の設定を復元することを覚えておく必要はありません。

9
Chuck Batson

これは1回限りのインポートなので、次のことができます。

  1. legacy_created_atおよびlegacy_updated_atフィールドを使用してモデルを作成します。
  2. レガシーデータをロードします。必要に応じてモデルフィールドにマップします。 #saveを使用でき、一般的にupdate_allなどの使用を心配する必要はありません。また、必要に応じてコールバックを使用できます。
  3. 列の名前をcreated_atおよびupdated_atに変更する移行を作成します。
3
runako

ミックスインモジュールを使用して、ブロックのタイムスタンプを一時的にオフにするのが好きです。

module WithoutTimestamps
  def without_timestamps
    old = ActiveRecord::Base.record_timestamps
    ActiveRecord::Base.record_timestamps = false
    begin
      yield
    ensure
      ActiveRecord::Base.record_timestamps = old
    end
  end
end

その後、必要な場所で使用できます

class MyModel < ActiveRecord::Base
  include WithoutTimestamps

  def save_without_timestamps
    without_timestamps do
      save!
    end
  end
end

または、次のように1回限りです:

m = MyModel.find(1)
WithoutTimestamps.without_timestamps do
  m.save!
end
3
nonrectangular

一括インポートではない場合、should_record_timestampsをオーバーライドできますか? updated_at列を更新するタイミングに関する新しいチェックを追加するモデルのメソッド。

2
iwiznia

他の回答を参照すると、次のように、単一のモデルのタイムスタンプを無効にすることに驚きました。

User.record_timestamps = false

私の開発用データベースでは機能しましたが、別のサーバーで実行される運用前データベースでは機能しませんでした。ただし、すべてのモデルのタイムスタンプを無効にすると機能します

ActiveRecord::Base.record_timestamps = false

(状況:移行でcreated_at属性を変更する)

2

できませんでしたActiveRecord::Base.record_timestampsフラグは何も変更せず、唯一の有効な方法はActiveRecord::Base.no_touching {}

ActiveRecord::Base.no_touching do
  Project.first.touch  # does nothing
  Message.first.touch  # does nothing
end

Project.no_touching do
  Project.first.touch  # does nothing
  Message.first.touch  # works, but does not touch the associated project
end

here から取得。

これは、ユーザーがcreated_atまたはupdated_at属性またはそれらは、ソート列として使用されます。

1
victorpolko

または、スレッドセーフについては、以下を参照してください: http://www.hungryfools.com/2007/07/turning-off-activerecord-timestamp.html

1
Andrew