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?
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
.
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.
update_column
est une fonction ActiveRecord
qui n'exécute aucun rappel et n'exécute pas non plus de validation.
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.
"
before_save :calculate_averages, if: Proc.new{ |survey| !survey.skip_callback }
"
"
def skip_callback(value = false)
@skip_callback = @skip_callback ? @skip_callback : value
end
"
"
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.
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)
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é
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é.
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