71

如果您有 DB 列created_atupdated_atRails 将在您创建和更新模型对象时自动设置这些值。有没有办法在不触及这些列的情况下保存模型?

我引入了一些遗留数据,我想从(不同命名的)遗留数据字段中的相应值设置这些值。我发现当我在模型上设置它们然后保存模型时,Rails 似乎覆盖了传入的值。

当然,我可以将 Rails 模型列命名为不同的名称以防止这种情况发生,但是在导入数据后,我希望 Rails 自动执行时间戳。

4

12 回答 12

81

在迁移或 rake 任务中执行此操作(如果您在边缘轨道上,则在新的数据库种子中):

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_atRails 不会抱怨。

注意:这也适用于单个模型,例如 User.record_timestamps = false

于 2009-05-14T13:31:49.027 回答
58

改用 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。

于 2012-06-24T21:30:49.403 回答
42

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
于 2016-05-12T17:06:56.863 回答
30

您可以在迁移中设置以下内容:

ActiveRecord::Base.record_timestamps = false

或者 altenatively 使用 update_all:

update_all(更新,条件 = nil,选项 = {})

如果它们与提供的一组条件匹配,则使用给定的详细信息更新所有记录,也可以提供限制和顺序。此方法构造单个 SQL UPDATE 语句并将其直接发送到数据库。它不会实例化涉及的模型,也不会触发 Active Record 回调。

于 2009-05-14T04:00:04.750 回答
13

在 Rails 3+ 中,对于单个对象,record_timestamps为对象而不是类设置。IE,

>> 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块中恢复先前的设置。

于 2016-07-20T22:19:17.893 回答
3

参考其他答案,我惊讶地发现禁用单个模型的时间戳,例如:

User.record_timestamps = false

为我的开发数据库工作,但不适用于在不同服务器上运行的预生产数据库。但是,如果我禁用所有模型的时间戳,它会起作用

ActiveRecord::Base.record_timestamps = false

(情况:在迁移中修改 created_at 属性)

于 2014-03-19T07:58:53.900 回答
3

由于这是一次性导入,您可以执行以下操作:

  1. legacy_created_at使用和legacy_updated_at字段创建模型。
  2. 加载旧数据。根据需要映射到模型字段。您可以使用#save并且通常不用担心使用update_all等,如果需要,您可以使用回调。
  3. 创建迁移以将列重命名为created_atupdated_at
于 2009-05-14T04:11:06.737 回答
3

我喜欢使用 mixin 模块来临时关闭块中的时间戳:

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

或者只是这样的一次性:

m = MyModel.find(1)
WithoutTimestamps.without_timestamps do
  m.save!
end
于 2013-10-21T21:12:32.387 回答
2

当不在批量导入中时,您可以覆盖 should_record_timestamps? 模型上的方法来添加关于何时更新 updated_at 列的新检查。

于 2013-10-08T22:39:13.393 回答
1

或者,对于线程安全,请参阅:http ://www.hungryfools.com/2007/07/turning-off-activerecord-timestamp.html

于 2010-12-16T00:22:57.247 回答
0

我无法让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

取自这里

这对于 rake 任务非常有用,您必须覆盖深度相关模型的某些属性并且不想打扰内部回调,而您的用户依赖created_atupdated_at属性或那些用作排序列。

于 2018-08-30T14:37:42.837 回答
0

我参加聚会迟到了,但这就是我们updated_at在商店中跳过更新的方式:

# config/initializers/no_timestamping.rb
module ActiveRecord
  class Base

    def update_record_without_timestamping
      class << self
        def record_timestamps; false; end
      end

      save!

      class << self
        remove_method :record_timestamps
      end
    end

  end
end

用法:

post.something = 'whatever'
post.update_record_without_timestamping
于 2019-09-26T19:31:26.343 回答