rails query left includes foreign create active ruby-on-rails ruby activerecord

ruby-on-rails - query - rails find



¿Cómo puedo evitar ejecutar callbacks de ActiveRecord? (25)

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.


¿Por qué querrías poder hacer esto en desarrollo? Seguramente, esto significará que está creando su aplicación con datos no válidos y, como tal, se comportará de forma extraña y no como espera en la producción.

Si quieres llenar tu db dev con datos, un mejor enfoque sería construir una tarea de rake que use la gema faker para compilar datos válidos e importarlos en el archivo db creando tantos o tan pocos registros como desees, pero si eres un tacón empeñado en eso y tengo una buena razón, supongo que update_without_callbacks y create_without_callbacks funcionarán bien, pero cuando trates de doblar los rieles a tu voluntad, pregúntate a ti mismo que tienes una buena razón y si lo que estás haciendo es realmente una buena idea.


Actualizado:

La solución de @Vikrant Chaudhary parece mejor:

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

Mi respuesta original:

vea este enlace: ¿Cómo omitir las devoluciones de llamada de ActiveRecord?

en Rails3,

supongamos que tenemos una definición de clase:

class User < ActiveRecord::Base after_save :generate_nick_name end

Enfoque1:

User.send(:create_without_callbacks) User.send(:update_without_callbacks)

Approach2: cuando quiera omitirlos en sus archivos rspec o lo que sea, intente esto:

User.skip_callback(:save, :after, :generate_nick_name) User.create!()

NOTA: una vez hecho esto, si no está en el entorno rspec, debe restablecer las devoluciones de llamada:

User.set_callback(:save, :after, :generate_nick_name)

funciona bien para mí en los carriles 3.0.5


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


Cuando necesito un control total sobre la devolución de llamada, creo otro atributo que se usa como un interruptor. Simple y efectivo:

Modelo:

class MyModel < ActiveRecord::Base before_save :do_stuff, unless: :skip_do_stuff_callback attr_accessor :skip_do_stuff_callback def do_stuff puts ''do stuff callback'' end end

Prueba:

m = MyModel.new() # Fire callbacks m.save # Without firing callbacks m.skip_do_stuff_callback = true m.save # Fire callbacks again m.skip_do_stuff_callback = false m.save


Escribí un complemento que implementa update_without_callbacks en Rails 3:

http://github.com/dball/skip_activerecord_callbacks

La solución correcta, creo, es reescribir sus modelos para evitar devoluciones de llamadas en primer lugar, pero si eso no es práctico en el corto plazo, este complemento puede ayudar.


Esta solución es Rails 2 solamente.

Acabo de investigar esto y creo que tengo una solución. Hay dos métodos privados de ActiveRecord que puede usar:

update_without_callbacks create_without_callbacks

Tendrás que usar enviar para llamar a estos métodos. ejemplos:

p = Person.new(:name => ''foo'') p.send(:create_without_callbacks) p = Person.find(1) p.send(:update_without_callbacks)

Esto es definitivamente algo que solo querrá usar realmente en la consola o mientras hace algunas pruebas al azar. ¡Espero que esto ayude!


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


La respuesta más up-voted puede parecer confusa en algunos casos.

Puede usar simplemente una verificación simple if desea omitir una devolución de llamada, como esta:

after_save :set_title, if: -> { !new_record? && self.name_changed? }


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)


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


No es la manera más limpia, pero podría envolver el código de devolución de llamada en una condición que verifique el entorno de Rails.

if Rails.env == ''production'' ...


Otra forma sería usar los ganchos de validación en lugar de las devoluciones de llamada. Por ejemplo:

class Person < ActiveRecord::Base validate_on_create :do_something def do_something "something clever goes here" end end

De esta forma, puede obtener el elemento do_algo por defecto, pero puede anularlo fácilmente con:

@person = Person.new @person.save(false)


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


Parece que una forma de manejar esto en Rails 2.3 (ya que update_without_callbacks se ha ido, etc.) sería usar update_all, que es uno de los métodos que omite las devoluciones de llamadas según la guides.rubyonrails.org/… .

Además, tenga en cuenta que si está haciendo algo en su devolución de llamada after_, que hace un cálculo basado en muchas asociaciones (es decir, has_many assoc, donde también acepta accept_nested_attributes_for), tendrá que volver a cargar la asociación, en el caso como parte del guardado , uno de sus miembros fue eliminado.


Puede intentar algo como esto en su modelo de persona:

after_save :something_cool, :unless => :skip_callbacks def skip_callbacks ENV[RAILS_ENV] == ''development'' # or something more complicated end

EDITAR: after_save no es un símbolo, pero esa es al menos la milésima vez que intento convertirlo en uno.


Puedes usar update_columns :

User.first.update_columns({:name => "sebastian", :age => 25})

Actualiza los atributos dados de un objeto, sin llamar a guardar, por lo tanto omitiendo validaciones y devoluciones de llamada.


Puedes usar la gema sneaky-save: https://rubygems.org/gems/sneaky-save .

Tenga en cuenta que esto no puede ayudar a guardar asociaciones junto sin validaciones. Lanza el error ''created_at no puede ser nulo'' ya que inserta directamente la consulta SQL a diferencia de un modelo. Para implementar esto, necesitamos actualizar todas las columnas generadas automáticamente de db.


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


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


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


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


# for rails 3 if !ActiveRecord::Base.private_method_defined? :update_without_callbacks def update_without_callbacks attributes_with_values = arel_attributes_values(false, false, attribute_names) return false if attributes_with_values.empty? self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values) end end