DB列がある場合created_at
およびupdated_at
Railsは、モデルオブジェクトを作成および更新するときに、これらの値を自動的に設定します。これらの列に触れずにモデルを保存する方法はありますか?
いくつかのレガシーデータを取り込んでいますが、(異なる名前の)レガシーデータフィールドの対応する値からそれらの値を設定したいと思います。モデルにそれらを設定してからモデルを保存すると、Railsが入力値をオーバーライドするように見えます。
もちろん、それを防ぐためにRailsモデル列に別の名前を付けることができますが、データをインポートした後、自動タイムスタンプ処理を行うためにRailsが必要です。
移行または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_at
とupdated_at
を手動で安全に設定できます。Railsは文句を言いません。
注:これは個々のモデルでも機能します。 User.record_timestamps = false
代わりに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を送出します。
Rails 5は、タイムスタンプを更新せずにレコードを更新する便利な方法を提供しますupdated_at
: https://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
移行内で次を設定できます。
ActiveRecord::Base.record_timestamps = false
または、代わりにupdate_allを使用します。
pdate_all(更新、条件= nil、オプション= {})
指定された条件のセットに一致する場合、指定された詳細ですべてのレコードを更新します。制限と順序も指定できます。このメソッドは、単一のSQL UPDATEステートメントを構築し、データベースに直接送信します。関連するモデルをインスタンス化せず、Active Recordコールバックをトリガーしません。
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
ブロックの以前の設定を復元することを覚えておく必要はありません。
これは1回限りのインポートなので、次のことができます。
legacy_created_at
およびlegacy_updated_at
フィールドを使用してモデルを作成します。#save
を使用でき、一般的にupdate_all
などの使用を心配する必要はありません。また、必要に応じてコールバックを使用できます。created_at
およびupdated_at
に変更する移行を作成します。ミックスインモジュールを使用して、ブロックのタイムスタンプを一時的にオフにするのが好きです。
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
一括インポートではない場合、should_record_timestampsをオーバーライドできますか? updated_at列を更新するタイミングに関する新しいチェックを追加するモデルのメソッド。
他の回答を参照すると、次のように、単一のモデルのタイムスタンプを無効にすることに驚きました。
User.record_timestamps = false
私の開発用データベースでは機能しましたが、別のサーバーで実行される運用前データベースでは機能しませんでした。ただし、すべてのモデルのタイムスタンプを無効にすると機能します
ActiveRecord::Base.record_timestamps = false
(状況:移行でcreated_at属性を変更する)
できませんでした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
属性またはそれらは、ソート列として使用されます。
または、スレッドセーフについては、以下を参照してください: http://www.hungryfools.com/2007/07/turning-off-activerecord-timestamp.html