有没有办法避免自动更新Rails时间戳字段?


69

如果您有数据库列created_atupdated_at那么在创建和更新模型对象时,Rails会自动设置这些值。有没有一种方法可以保存模型而不接触那些列?

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

当然,我可以使用不同的命名方式来命名Rails模型列,以防止这种情况,但是在导入数据之后,我希望Rails执行其自动时间戳记功能。

Answers:


79

在迁移或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_at手动操作,Rails不会抱怨。

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


3
您甚至可以将其引入方法中,例如def without_timestamps old = ActiveRecord::Base.record_timestamps ActiveRecord::Base.record_timestamps = false begin yield ensure ActiveRecord::Base.record_timestamps = old end end
2013年

1
true即使在控制台内执行脚本或代码时,我们是否还需要将其重置回?
oldgod 2014年

2
我认为stackoverflow.com/a/37193298/1214748更好。它不会在整个应用程序中全局关闭它
maschwenk

@maschwenk是的,对于rails 5是正确的,但是对于那些被迫使用较旧版本的rails的用户,这似乎仍然是最佳选择。
莫里斯

无论如何,我都不会想过:) iirc会在应用程序启动之前运行迁移,所以没关系。(自从我做滑轨已经有一段时间了,如果我错了,请纠正我)
August Lilleaas

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。


请注意,对于序列化的列,您必须自己自行设置值。
贾里德(Jared)

39

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

1
到目前为止,这是移动记录的最干净的方法。
John Doe

似乎有点奇怪,相同的选项在做时不起作用update。您需要将其分为两个操作,如下所示。
MSC

30

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

ActiveRecord::Base.record_timestamps = false

或交替使用update_all:

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

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


12

在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
在Rails 5之前进行单个更新时,这是最干净的方法
。– Phitherek_

3

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

  1. 使用legacy_created_atlegacy_updated_at字段创建模型。
  2. 加载旧数据。根据需要映射到模型字段。您可以使用#save并且通常不用担心使用update_all或类似方法,并且可以根据需要使用回调。
  3. 创建迁移以将列重命名为created_atupdated_at

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

2
只是警告,这不是线程安全的。最近被这个咬了!
Slicedpan 2015年

3

提到其他答案,令我惊讶的是,禁用了单个模型的时间戳,例如:

User.record_timestamps = false

适用于我的开发数据库,​​但不适用于在其他服务器上运行的预生产数据库。但是,如果我禁用所有型号的时间戳,它将起作用

ActiveRecord::Base.record_timestamps = false

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


2

如果不批量导入,则可以覆盖should_record_timestamps?模型上的方法,以添加有关何时更新Updated_at列的新检查。



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任务非常有用,在rake任务中,您必须重写与深度相关的模型的某些属性,并且不想打扰内部回调,而用户则依赖created_atupdated_at属性,或者将这些属性用作排序列。


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.