在Rails中手动设置updated_at

时间:2009-09-06 19:32:30

标签: ruby-on-rails callback

我正在将旧的博客帖子迁移到我的新Rails博客中,我希望他们的updated_at属性与我旧博客上的相应值相匹配(而不是他们迁移到我的新Rails博客的日期)。

我该怎么做?当我手动设置updated_at时,它会被before_save回调覆盖。

注意:此问题仅适用于Rails< 3.2.11。较新版本的Rails允许您手动设置时间戳,而不会覆盖它们。

6 个答案:

答案 0 :(得分:28)

如果是一次性的话,你可以打开或关闭record_timestamps

ActiveRecord::Base.record_timestamps = false

#set timestamps manually

ActiveRecord::Base.record_timestamps = true

当我用我的应用程序遇到这个问题时,我搜索了一下,这似乎对我来说最有意义。这是一个初始化程序,我可以在需要的地方调用:

module ActiveRecord  
  class Base  

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

      save!  

      class << self  
        def record_timestamps; super ; end  
      end  
    end  

  end  
end  

答案 1 :(得分:5)

从Rails的最新版本(根据iGEL评论3.2.11)开始,您可以在代码中设置updated_at属性,并在保存时遵守更改。

我认为rails正在跟踪手动更改的“脏”属性,而不是在保存时覆盖。

> note = Note.last
  Note Load (1.4ms)  SELECT "notes".* FROM "notes" ORDER BY "notes"."id" DESC LIMIT 1
=> #<Note id: 39, content: "A wee note", created_at: "2015-06-09 11:06:01", updated_at: "2015-06-09 11:06:01">
> note.updated_at = 2.years.ago
=> Sun, 07 Jul 2013 21:20:47 UTC +00:00
> note.save
   (0.4ms)  BEGIN
   (0.8ms)  UPDATE "notes" SET "updated_at" = '2013-07-07 21:20:47.972990' WHERE "notes"."id" = 39
   (0.8ms)  COMMIT
=> true
> note
=> #<Note id: 39, content: "A wee note",  created_at: "2015-06-09 11:06:01", updated_at: "2013-07-07 21:20:47">

如此简短的回答,在最新版本的rails中不再需要解决方法。

答案 2 :(得分:3)

我接受了Andy的回答并修改了它以接受块:

module ActiveRecord
  class Base

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

      yield

      class << self
        remove_method :record_timestamps
      end
    end

  end
end

答案 3 :(得分:2)

这是Andy Gaskell的回答:

class ActiveRecord::Base
  class_inheritable_writer :record_timestamps
  def do_without_changing_timestamps
    self.class.record_timestamps = false
    yield
  ensure
    self.class.record_timestamps = true
  end
end

答案 4 :(得分:1)

我认为有两种方法可以轻松实现这一目标:

触摸(Rails 5)

在Rails 5中,您可以使用touch方法并提供the documentation of touch

中所述的命名参数time
foo.touch(time: old_timestamp)

update_column(Rails&gt; = 3.1)

如果你想在Rails 4及更低版本中使用它或想要避免所有回调,你可以使用update_columnupdate_columns方法之一bypass all safe or touch callbacks and validations

foo.update_column(updated_at, old_timestamp)

foo.update_columns(updated_at: old_timestamp)

答案 5 :(得分:0)

解决方案是暂时将ActiveRecord :: Base.record_timestamps设置为false:

ActiveRecord::Base.record_timestamps = false

# Make whatever changes you want to the timestamps here

ActiveRecord::Base.record_timestamps = true

如果你想要一个更强大的解决方案,你可能想尝试像mrm建议的那样:

module ActiveRecord
  class Base

    def self.without_timestamping
      timestamping = self.record_timestamps
      begin
        self.record_timestamps = false
        yield
      ensure
        self.record_timestamps = timestamping
      end 
    end

  end
end

然后,您可以轻松地对模型进行更改,而不会自动更新时间戳:

ActiveRecord::Base.without_timestamping do
  foo = Foo.first
  bar = Bar.first
  foo.updated_at = 1.month.ago
  bar.updated_at = foo.updated_at + 1.week
  foo.save!
  bar.save!
end

或者,如果您只想更新特定类中的记录而不加时间戳:

module ActiveRecord
  class Base
    # Don't delete Rail's ActiveRecord::Base#inherited method
    self.singleton_class.send(:alias_method, :__inherited__, :inherited)
    def self.inherited(subclass)
      __inherited__

      # Adding class methods to `subclass`
      class << subclass

        def without_timestamping
          # Temporarily override record_timestamps for this class
          class << self
            def record_timestamps; false; end
          end
          yield
        ensure
          class << self
            remove_method :record_timestamps
          end
        end

      end

    end
  end
end

E.g:

Foo.without_timestamping do
  foo = Foo.first
  bar = Bar.new(foo: foo)
  foo.updated_at = 1.month.ago
  foo.save! # Timestamps not automatically updated
  bar.save! # Timestamps updated normally
end

或者您可以使用类似于Venkat D.建议的方法,该方法适用于每个实例:

module ActiveRecord
  class Base

    def without_timestamping
      class << self
        def record_timestamps; false; end
      end
      yield
    ensure
      class << self
        remove_method :record_timestamps
      end
    end

  end
end

E.g:

foo = Foo.first
foo.without_timestamping do
  foo2 = Foo.new(parent: foo)
  foo.updated_at = 1.month.ago
  foo.save! # Timestamps not automatically updated
  foo2.save! # Timestamps updated normally
end