[ruby-on-rails] ¿Cómo puedo evitar ejecutar callbacks de ActiveRecord?


Answers

Utilice update_column (Rails> = v3.1) o update_columns (Rails> = 4.0) para omitir las devoluciones de llamada y validaciones. También con estos métodos, updated_at no se actualiza.

#Rails >= v3.1 only
@person.update_column(:some_attribute, 'value')
#Rails >= v4.0 only
@person.update_columns(attributes)

http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column

# 2: omisión de devoluciones de llamada que también funciona al crear un objeto

class Person < ActiveRecord::Base
  attr_accessor :skip_some_callbacks

  before_validation :do_something
  after_validation :do_something_else

  skip_callback :validation, :before, :do_something, if: :skip_some_callbacks
  skip_callback :validation, :after, :do_something_else, if: :skip_some_callbacks
end

person = Person.new(person_params)
person.skip_some_callbacks = true
person.save
Question

Tengo algunos modelos que tienen callbacks after_save. Por lo general, eso está bien, pero en algunas situaciones, como cuando se crean datos de desarrollo, quiero guardar los modelos sin ejecutar las devoluciones de llamadas. ¿Hay una manera simple de hacer eso? Algo parecido a ...

Person#save( :run_callbacks => false )

o

Person#save_without_callbacks

Busqué en los documentos de Rails y no encontré nada. Sin embargo, en mi experiencia, los documentos de Rails no siempre cuentan toda la historia.

ACTUALIZAR

Encontré una publicación de blog que explica cómo puedes eliminar devoluciones de llamada de un modelo como este:

Foo.after_save.clear

No pude encontrar dónde está documentado ese método, pero parece funcionar.




Para crear datos de prueba en Rails, usa este truco:

record = Something.new(attrs)
ActiveRecord::Persistence.instance_method(:create_record).bind(record).call

https://coderwall.com/p/y3yp2q/edit




La única forma de evitar todas las devoluciones de llamada after_save es hacer que la primera devuelva false.

Tal vez podrías probar algo como (no probado):

class MyModel < ActiveRecord::Base
  attr_accessor :skip_after_save

  def after_save
    return false if @skip_after_save
    ... blah blah ...
  end
end

...

m = MyModel.new # ... etc etc
m.skip_after_save = true
m.save



Algo que debería funcionar con todas las versiones de ActiveRecord sin depender de opciones o métodos activerecord que pueden existir o no.

module PlainModel
  def self.included(base)
    plainclass = Class.new(ActiveRecord::Base) do
      self.table_name = base.table_name
    end
    base.const_set(:Plain, plainclass)
  end
end


# usage
class User < ActiveRecord::Base
  include PlainModel

  validates_presence_of :email
end

User.create(email: "")        # fail due to validation
User::Plain.create(email: "") # success. no validation, no callbacks

user = User::Plain.find(1)
user.email = ""
user.save

TLDR: utilice un "modelo de registro activo diferente" en la misma tabla




Carriles 3:

MyModel.send("_#{symbol}_callbacks") # list  
MyModel.reset_callbacks symbol # reset



https://gist.github.com/576546

simplemente descargue este parche de mono en config / initializers / skip_callbacks.rb

entonces

Project.skip_callbacks { @project.save }

o similar.

todo el crédito al autor




Una solución que debería funcionar en todas las versiones de Rails sin el uso de una gema o complemento es simplemente emitir instrucciones de actualización directamente. p.ej

ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}"

Esto puede (o no) ser una opción dependiendo de qué tan compleja sea su actualización. Esto funciona bien para, por ejemplo, actualizar indicadores en un registro desde una devolución de llamada after_save (sin redisparar la devolución de llamada).




Necesitaba una solución para Rails 4, así que se me ocurrió esto:

app / models / concerns / save_without_callbacks.rb

module SaveWithoutCallbacks

  def self.included(base)
    base.const_set(:WithoutCallbacks,
      Class.new(ActiveRecord::Base) do
        self.table_name = base.table_name
      end
      )
  end

  def save_without_callbacks
    new_record? ? create_without_callbacks : update_without_callbacks
  end

  def create_without_callbacks
    plain_model = self.class.const_get(:WithoutCallbacks)
    plain_record = plain_model.create(self.attributes)
    self.id = plain_record.id
    self.created_at = Time.zone.now
    self.updated_at = Time.zone.now
    @new_record = false
    true
  end

  def update_without_callbacks
    update_attributes = attributes.except(self.class.primary_key)
    update_attributes['created_at'] = Time.zone.now
    update_attributes['updated_at'] = Time.zone.now
    update_columns update_attributes
  end

end

en cualquier modelo:

include SaveWithoutCallbacks

entonces tú puedes:

record.save_without_callbacks

o

Model::WithoutCallbacks.create(attributes)



Si el objetivo es simplemente insertar un registro sin devoluciones de llamada o validaciones, y le gustaría hacerlo sin recurrir a gemas adicionales, agregar comprobaciones condicionales, usar SQL RAW o actualizar su código de salida de alguna manera, considere usar una "sombra objeto "apuntando a su tabla db existente. Al igual que:

class ImportedPerson < ActiveRecord::Base
  self.table_name = 'people'
end

Esto funciona con todas las versiones de Rails, es seguro para los hilos y elimina por completo todas las validaciones y devoluciones de llamadas sin modificaciones a su código existente. Puede lanzar esa declaración de clase justo antes de su importación real, y debería estar listo para comenzar. Solo recuerde usar su nueva clase para insertar el objeto, como:

ImportedPerson.new( person_attributes )



Si está utilizando Rails 2. Puede usar la consulta SQL para actualizar su columna sin ejecutar devoluciones de llamada y validaciones.

YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}")

Creo que debería funcionar en cualquier versión de rieles.




Una opción es tener un modelo separado para tales manipulaciones, usando la misma tabla:

class NoCallbacksModel < ActiveRecord::Base
  set_table_name 'table_name_of_model_that_has_callbacks'

  include CommonModelMethods # if there are
  :
  :

end

(El mismo enfoque puede facilitar las cosas para pasar las validaciones)

Stephan




Ninguno de estos puntos al complemento without_callbacks que solo hace lo que necesita ...

class MyModel < ActiveRecord::Base
  before_save :do_something_before_save

  def after_save
    raise RuntimeError, "after_save called"
  end

  def do_something_before_save
    raise RuntimeError, "do_something_before_save called"
  end
end

o = MyModel.new
MyModel.without_callbacks(:before_save, :after_save) do
  o.save # no exceptions raised
end

http://github.com/cjbottaro/without_callbacks funciona con Rails 2.x






Related