Подтвердить что ты не робот

Вручную установите update_at в Rails

Я переношу свои старые сообщения в блоге в свой новый Rails-блог, и я хочу, чтобы их атрибут updated_at соответствовал соответствующему значению в моем старом блоге (а не дату, когда они были перенесены в мой новый блог Rails).

Как я могу это сделать? Когда я устанавливаю updated_at вручную, он переопределяется обратным вызовом before_save.

Примечание: Этот вопрос действителен только для Rails < 3.2.11. Новые версии Rails позволяют вручную устанавливать временные метки без их перезаписывания.

4b9b3361

Ответ 1

Если это один раз, вы можете включить или отключить 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  

Ответ 2

Как и в случае с последними версиями Rails (3.2.11 в соответствии с комментарием iGELs), вы можете установить свойство updated_at в коде, и при сохранении это будет выполнено.

Я предполагаю, что рельсы отслеживают "грязные" свойства, которые были вручную изменены, а не перезаписываются при сохранении.

> 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">

Такой короткий ответ, обходные решения больше не нужны в последних версиях рельсов.

Ответ 3

Я взял Энди и изменил его, чтобы принять блоки:

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

Ответ 4

Это отрывается от ответа Энди Гаскелла:

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

Ответ 5

Решение состоит в том, чтобы временно установить значение 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 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

например:

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

например:

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

Ответ 6

Я вижу два способа сделать это легко:

touch (Rails 5)

В Rails 5 вы можете использовать метод touch и дать именованный параметр time, как описано в документация touch

foo.touch(time: old_timestamp)

update_column (Rails >= 3.1)

Если вы хотите его в Rails 4 и ниже или хотите избежать всех обратных вызовов, вы можете использовать один из методов update_column или update_columns, которые обходит все безопасные или косвенные обратные вызовы и проверки

foo.update_column(updated_at, old_timestamp)

или

foo.update_columns(updated_at: old_timestamp)