Есть ли способ избежать автоматического обновления полей меток времени в Rails? - PullRequest
63 голосов
/ 14 мая 2009

Если у вас есть столбцы БД created_at и updated_at, Rails автоматически установит эти значения при создании и обновлении объекта модели. Есть ли способ сохранить модель, не касаясь этих столбцов?

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

Конечно, я мог бы просто по-разному называть столбцы модели Rails, чтобы предотвратить это, но после того, как данные импортированы, я хочу, чтобы Rails делал автоматическую отметку времени.

Ответы [ 11 ]

71 голосов
/ 14 мая 2009

Сделайте это в миграции или в задаче с граблями (или в новых семенах базы данных , если вы находитесь на рельсах):

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

54 голосов
/ 25 июня 2012

используйте метод 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 не обновляется, если этот столбец доступен.

Вызывает ActiveRecordError, когда вызывается для новых объектов или когда атрибут name помечается как доступный только для чтения.

35 голосов
/ 12 мая 2016

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
30 голосов
/ 14 мая 2009

Вы можете установить следующее внутри вашей миграции:

ActiveRecord::Base.record_timestamps = false

Или альтернативно использовать update_all:

update_all (обновления, условия = ноль, опции = {})

Обновляет все записи с указанием деталей если они соответствуют ряду условий поставка, ограничения и порядок также могут быть в комплект поставки. Этот метод создает один оператор SQL UPDATE и отправляет это прямо в базу данных. Оно делает не создавать экземпляры участвующих моделей и это не вызывает активную запись Обратные вызовы.

9 голосов
/ 21 июля 2016

В 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.

3 голосов
/ 22 октября 2013

Мне нравится использовать модуль 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
3 голосов
/ 14 мая 2009

Поскольку это однократный импорт, вы можете сделать следующее:

  1. Создать модель, используя поля legacy_created_at и legacy_updated_at.
  2. Загрузка устаревших данных. Карта в поля модели по желанию. Вы можете использовать #save и, как правило, не беспокоиться об использовании update_all и т.п., и при желании вы можете использовать обратные вызовы.
  3. Создание миграции для переименования столбцов в created_at и updated_at.
2 голосов
/ 19 марта 2014

Обращаясь к другим ответам, я с удивлением обнаружил, что отключение меток времени для одной модели, например, в:

User.record_timestamps = false

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

ActiveRecord::Base.record_timestamps = false

(Ситуация: изменение атрибута созданного в миграции)

2 голосов
/ 09 октября 2013

Когда нет массового импорта, вы можете переопределить should_record_timestamps? метод в вашей модели, чтобы добавить новые проверки, когда обновлять столбец updated_at.

1 голос
/ 30 августа 2018

Я не смог заставить флаг 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_at или updated_at или те, которые используются в качестве сортирующих столбцов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...