23

Ho un before_save nel mio modello Message definita in questo modo:Come "update_attributes" senza eseguire "before_save"?

class Message < ActiveRecord::Base 
    before_save lambda { foo(publisher); bar } 
    end 

Quando faccio: sono eseguiti

my_message.update_attributes(:created_at => ...) 

foo e bar.

A volte, desidero aggiornare i campi del messaggio senza eseguire foo e bar.

Come è possibile aggiornare, ad esempio, il campo created_at (nel database) senza eseguire foo e bar?

risposta

33

In rail 3.1 userete update_column.

Altrimenti:

In linea generale, il modo più elegante per bypassare i callback è il seguente:

class Message < ActiveRecord::Base 
    cattr_accessor :skip_callbacks 
    before_save lambda { foo(publisher); bar }, :unless => :skip_callbacks # let's say you do not want this callback to be triggered when you perform batch operations 
end 

Poi, si può fare:

Message.skip_callbacks = true # for multiple records 
my_message.update_attributes(:created_at => ...) 
Message.skip_callbacks = false # reset 

Oppure, per una sola record:

my_message.update_attributes(:created_at => ..., :skip_callbacks => true) 

Se ne avete bisogno specificatamente per un attributo Time, allora touch farà il trucco come menzionato da @lucapette.

+0

Sembra una buona soluzione generale! Una domanda: cosa fa esattamente 'Message.batch = true'? –

+0

È solo una bandiera. Puoi sostituirlo con qualsiasi cosa tu voglia. – jbescoyez

+0

Ho aggiornato l'esempio con un flag più esplicito. – jbescoyez

17

update_all non si innescherà callback

my_message.update_all(:created_at => ...) 
# OR 
Message.update_all({:created_at => ...}, {:id => my_message.id}) 

http://apidock.com/rails/ActiveRecord/Base/update_all/class

+1

'my_message.update_all (: created_at => ...)' genera un errore di sintassi, ma la seconda opzione funziona correttamente! –

+1

my_message.update_all attiverà 'metodo non definito update_all'. Message.update_all farà il trucco –

+0

Potresti essere in grado di utilizzare increment_counter [se si tratta di un contatore che vuoi incrementare] che salta anche i callback, credo. – rogerdpack

6

Utilizzare il metodo touch. È elegante e fa esattamente quello che vuoi

+0

Sembra quasi il secondo di ciò che mi serve. Il nuovo valore di 'created_at' nel mio caso è ** non ** l'ora corrente. –

+0

Oh, interessante :) – fl00r

+0

@Misha hai ovviamente ragione. Quindi puoi usare http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_attribute: D – lucapette

1

È anche possibile condizionare l'azione before_save.

Quindi aggiungere qualche variabile campo/istanza e impostarlo solo se si desidera ignorarlo e verificarlo nel metodo.

E.g.

before_save :do_foo_and_bar_if_allowed 

attr_accessor :skip_before_save 

def do_foo_and_bar_if_allowed 
    unless @skip_before_save.present? 
    foo(publisher) 
    bar 
    end 
end 

e poi da qualche parte scrivono

my_message.skip_before_save = true 
my_message.update_attributes(:created_at => ...) 
0

update_column o update_columns è il metodo più vicino alla update_attributes ed evita callback senza dover aggirare manualmente nulla.