web-dev-qa-db-fra.com

Comment enregistrer un modèle sans exécuter de rappels dans Rails

J'ai besoin de calculer des valeurs lors de l'enregistrement d'un modèle dans Rails. Alors j'appelle calculate_averages comme rappel pour une classe Survey:

before_save :calculate_averages

Cependant, à l'occasion (et au départ, j'ai 10 000 enregistrements qui nécessitent cette opération), je dois mettre à jour manuellement toutes les moyennes pour chaque enregistrement. Pas de problème, j'ai du code comme celui-ci:

Survey.all.each do |survey|
  survey.some_average = (survey.some_value + survey.some_other_value) / 2.to_f
  #and some more averages...
  survey.save!
end

Avant même d'exécuter ce code, je crains que le calculate_averages va être appelé et dupliquer cela et probablement même causer des problèmes avec la façon dont je fais les choses. Ok, alors je pense, eh bien je vais juste ne rien faire et laisser calculate_averages se faire appeler et faire son truc. Problème il y a d'abord un moyen de forcer les rappels à être appelés même si vous n'avez apporté aucune modification à l'enregistrement?

Deuxièmement, la façon dont les moyennes sont calculées est loin plus efficace pour ne pas laisser les rappels être appelés du tout et faire les moyennes pour tout en une seule fois. Est-il possible de ne pas laisser les rappels être appelés?

24
at.

Je crois que ce que vous demandez peut être réalisé avec ActiveSupport::Callbacks . Jettes un coup d'oeil à set_callback et skip_callback.

Afin de "forcer les rappels à être appelés même si vous n'avez apporté aucune modification à l'enregistrement", vous devez enregistrer le rappel à un événement, par ex. save, validate etc..

set_callback :save, :before, :my_before_save_callback

Pour ignorer le before_save rappel, vous feriez:

Survey.skip_callback(:save, :before, :calculate_average). 

Veuillez référencer le lien ActiveSupport::Callbacks sur les autres options prises en charge telles que les conditions et les blocages à set_callback et skip_callback.

23
vee

Pour désactiver les rappels en masse, utilisez ...

Survey.skip_callback(:save, :before, :calculate_averages)

Puis pour les activer ...

Survey.set_callback(:save, :before, :calculate_average)

Cela saute/définit pour toutes les instances.

15
rafroehlich2

update_column est une fonction ActiveRecord qui n'exécute aucun rappel et n'exécute pas non plus de validation.

10
Ahmad Hussain

Si vous souhaitez ignorer les rappels conditionnels après avoir vérifié pour chaque enquête, vous pouvez écrire votre méthode personnalisée.

Par exemple.

  • Rappel modifié-

"

before_save :calculate_averages, if: Proc.new{ |survey| !survey.skip_callback }

"

  • Nouvelle méthode d'instance-

"

def skip_callback(value = false)
  @skip_callback = @skip_callback ? @skip_callback : value
end

"

  • Script pour mettre à jour les sondages-

"

Survey.all.each do |survey|
  survey.some_average = (survey.some_value + survey.some_other_value) / 2.to_f
  #and some more averages...
  survey.skip_callback(true)
  survey.save!
end

"

C'est un peu hack mais l'espoir fonctionnera pour vous.

4
Swaps

Ne fonctionne pas pour Rails 5

Survey.skip_callback(:save, :before, :calculate_average) 

Fonctionne pour Rails 5

Survey.skip_callback(:save, :before, :calculate_average, raise: false)

https://github.com/thoughtbot/factory_bot/issues/931

4
dan987

Pour Rails 3 ActiveSupport::Callbacks vous donne le contrôle nécessaire. Vous pouvez reset_callbacks en masse, ou utilisez skip_callback pour désactiver judicieusement comme ceci:

Vote.skip_callback(:save, :after, :add_points_to_user)

… Après quoi vous pouvez opérer sur les instances de vote avec :add_points_to_user inhibé

2
Rafeeq

j'espère que c'est ce que vous cherchez.

https://stackoverflow.com/a/6587546/2238259

Pour votre deuxième problème, je soupçonne qu'il serait préférable d'inspecter quand ce calcul doit se produire, il serait préférable qu'il puisse être traité par lots à une heure spécifiée où le trafic réseau est à son creux.

EDIT: Woops. En fait, j'ai trouvé 2 liens mais j'ai perdu le premier, apparemment. J'espère que vous l'avez réparé.

2
Sherwyn Goh

Rails 5.2.3 nécessitant un script after party pour NE PAS déclencher d'événements de modèle, update_column (nom_colonne, valeur) a fait l'affaire:

task.update_column(task_status, ReferenceDatum::KEY_COMPLETED)

https://apidock.com/Rails/ActiveRecord/Persistence/update_column

1
FlimFlam Vir