2011-09-27 11 views
66

Ho un modello particolarmente complesso con convalide e callback definiti. Il business necessita ora di uno scenario particolare in cui aggiungere un nuovo record richiede di saltare le convalide e i callback. Qual'è il miglior modo per farlo?Rails 3 salta convalide e callback

risposta

116

Questo funziona in Rails 3:

Model.skip_callback(:create) 
model.save(:validate => false) 
Model.set_callback(:create) 

(API docs e related question)

+5

Non hai ancora bisogno di usare '' 'set_callback (: create)' ''? Ho avuto l'impressione che '' 'skip_callback''' lo disabiliti fino a quando non viene riattivato. –

+1

Penso di sì, non uso mai questo metodo, maggiori informazioni http://api.rubyonrails.org/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-skip_callback – Dinatih

+19

Questo file non può essere protetto? sarebbe bello poterlo impostare su un livello di istanza .. –

1

Che sull'aggiunta di un metodo per il vostro modello che ti permette di saltare i callback?

class Foo < ActiveRecord::Base 
    after_save :do_stuff 

    def super_secret_create(attrs) 
    self.skip_callback(:create) 
    self.update_attributes(attrs) 
    self.save(:validate => false) 
    self.set_callback(:create) 
    end 
end 

Se si finisce per utilizzare qualcosa come questo, mi consiglia di utilizzare auto nel metodo al posto del nome del modello per evitare conascenza di nome.

Ho anche imbattuto in un succo di Sven Fuchs che sembra piacevole, è here

+1

Mi piace questo approccio ma 'skip_callback' viene mostrato come un metodo di classe mentre 'update_attributes' è un metodo di istanza, è quello che intendevi? Hmm non capisco, il doc dell'API dice 'skip_callback' è un metodo di istanza ma non è così nella console di Rails, strano. –

2

Il mio introito era come questo (nota: questo disabilita callback su Crea, per aggiornare, cancellare e gli altri è necessario aggiungere loro di gamma).

begin 
     [:create, :save].each{|a| self.class.skip_callback(a) } # We disable callbacks on save and create 

     # create new record here without callbacks, tou can also disable validations with 
     # .save(:validate => false) 
    ensure 
     [:create, :save].each{|a| self.class.set_callback(a) } # and we ensure that callbacks are restored 
    end 
+0

In suvated, ma poi non ha funzionato per la mia app 3.2.6. Stavo chiamando Model.skip_callback (: create,: after) – TuteC

+0

Questa è la soluzione per 3.0+ Non funzionerà su 2.x - ma ho ricordato che quando stavo cercando una soluzione a questo mi sono imbattuto in soluzione per 2 .x che non ha funzionato per 3.x –

29

Usa ActiveRecord::Persistence#update_column, in questo modo:

Model.update_column(field, value) 
+0

In base alla documentazione collegata: "I callback vengono richiamati." – eggie5

+5

Penso che abbiate consultato la documentazione di 'update_attribute', non di' update_column'. Se si guarda la documentazione di 'update_column', è equivalente a' update_columns' per una singola colonna. I documenti per 'update_columns' indicano chiaramente che le convalide e le callback sono saltate. – bowsersenior

+0

Salvami la vita! Ho provato skip_callback e altri metodi, senza fortuna. Funziona! –

0

Questo mod ha funzionato per me, finalmente (ridefinito _notify_comment_observer_for_after_create metodo per l'oggetto):

if no_after_create_callback 
    def object._notify_comment_observer_for_after_create; nil; end 
end 
1

ho scritto un semplice gioiello per saltare convalide ad hoc, ma potrebbe probabilmente essere aggiornato per includere anche le richiamate saltate.

https://github.com/npearson72/validation_skipper

Si potrebbe prendere il can_skip_validation_for nella gemma e aggiungere funzionalità per saltare anche le richiamate. Forse chiama il metodo can_skip_validation_and_callbacks_for

Tutto il resto funzionerebbe allo stesso modo. Se vuoi aiuto per farlo, fammi sapere.

1

Si consiglia di NON utilizzare l'approccio skip_callback poiché non è thread-safe. Il sneaky save gem tuttavia è poiché esegue semplicemente SQL. Nota che ciò non attiverà le convalide, quindi dovrai chiamarle tu stesso (es: my_model.valid?).

Ecco alcuni esempi dai loro documenti:

# Update. Returns true on success, false otherwise. 
existing_record.sneaky_save 

# Insert. Returns true on success, false otherwise. 
Model.new.sneaky_save 

# Raise exception on failure. 
record.sneaky_save! 
7

Se l'obiettivo è quello di inserire semplicemente o aggiornare un record senza callback o convalide, e si vorrebbe farlo senza ricorrere a gemme aggiuntive, aggiungendo condizionale controlla, usando RAW SQL, o futzing con il tuo codice in uscita in qualsiasi modo, può essere possibile utilizzare un "oggetto ombra" che punta alla tabella db esistente. In questo modo:

class ImportedUser < ActiveRecord::Base 
    # To import users with no validations or callbacks 
    self.table_name = 'users' 
end 

Questo funziona con tutte le versioni di Rails, è threadsafe, ed elimina completamente tutte le convalide e callback senza modifiche al codice esistente.Basta ricordarsi di utilizzare la nuova classe per inserire l'oggetto, come:

ImportedUser.new(person_attributes) 
+1

questo è così intelligente. mi hai salvato mal di testa! –

0

Nessuno di questi funzionerà se le vostre convalide sono scritti nel database stesso.

+------------------------------------+--------------------------------------------------+------+-----+--------------------+----------------+ 
| Field        | Type            | Null | Key | Default   | Extra   | 
+------------------------------------+--------------------------------------------------+------+-----+--------------------+----------------+ 
| status        | enum('Big','Small','Ugly','Stupid','Apologetic') | NO |  | Stupid    |    |