ruby-on-rails - validations - validates activerecord
Establecer manualmente updated_at en Rails (6)
Estoy migrando mis publicaciones de blog antiguas a mi nuevo blog de Rails, y quiero que su atributo updated_at
coincida con el valor correspondiente en mi blog anterior (no la fecha en que se migraron a mi nuevo blog de Rails).
¿Cómo puedo hacer esto? Cuando configuro updated_at
manualmente, se invalida con la before_save
llamada before_save
.
Nota: esta pregunta solo es válida para Rails <3.2.11. Las versiones más nuevas de Rails le permiten configurar manualmente las marcas de tiempo sin que se sobrescriban.
A partir de las versiones recientes de Rails (3.2.11 según el comentario de iGELs), puede establecer la propiedad updated_at en el código y el cambio se respetará al guardar.
Supongo que los rieles mantienen un registro de las propiedades "sucias" que se han cambiado manualmente y no se sobrescriben al guardar.
> 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">
Entonces, respuesta corta, las soluciones temporales ya no son necesarias en las versiones recientes de rieles.
Esto es una copia de la respuesta de 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
La solución es configurar temporalmente ActiveRecord :: Base.record_timestamps en falso:
ActiveRecord::Base.record_timestamps = false
# Make whatever changes you want to the timestamps here
ActiveRecord::Base.record_timestamps = true
Si desea una solución algo más robusta, puede intentar algo como lo que sugirió 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
Luego, puede realizar cambios fácilmente en los modelos sin que sus marcas de tiempo se actualicen automáticamente:
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
O, si solo desea actualizar los registros de una clase específica sin marca de tiempo:
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
P.ej:
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
O puede utilizar un enfoque similar al que sugirió Venkat D., que funciona por instancia:
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
P.ej:
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
Si es una cosa de una sola vez, puedes activar o desactivar el record_timestamps
.
ActiveRecord::Base.record_timestamps = false
#set timestamps manually
ActiveRecord::Base.record_timestamps = true
Cuando me topé con este problema con mi aplicación, busqué un poco y me pareció que tenía más sentido para mí. Es un inicializador al que puedo llamar donde necesito:
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
Tomé la respuesta de Andy y la modifiqué para aceptar bloques:
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
Veo dos maneras de lograr esto fácilmente:
toque (rieles 5)
En Rails 5 puede usar el método touch
y dar un time
parámetro nombrado como se describe en la documentación de contacto.
foo.touch(time: old_timestamp)
update_column (Rails> = 3.1)
Si lo desea en Rails 4 e inferior o si desea evitar todas las devoluciones de llamada, puede utilizar uno de los métodos update_column
o update_columns
que omiten todas las devoluciones de llamada seguras o táctiles y las validaciones
foo.update_column(updated_at, old_timestamp)
o
foo.update_columns(updated_at: old_timestamp)